type
status
date
slug
summary
tags
category
icon
password
我们在针对
Block
的剖析在进一步加深,了解了Block
如何截获基本类型,了解Block的类型和copy
操作,下面我们开始进入Block的内存管理世界。Block的内存管理,主要针对捕获外部对象类型的
auto
变量。根据Block捕获auto基本变型的规律,针对对象,仍然适用。
auto
变量捕获后,Block
中变量的类型和变量原类型一致;
static
变量捕获后,Block
对应的变量是对应变量的指针类型;
那么,
auto
对象与基本类型在Block内部有什么区别呢。我们将从两方面讨论:
Block
是如何捕获对象类型的?
Block
内部是如何管理对象类型的?
一、捕获对象类型
以下代码位于Block捕获对象类型-
Test.m
中。1. Block
对象结构体
将下面代码重写:
以上
Block
对象重写后的结构体如下:可以看到:
Block
对象仍然捕获auto
变量后,保留了person
对象的类型。2.Desc
及Func
进一步观察
Block
对象中的Desc
结构以及Func
:那么与基本类型的捕获区别在哪里呢?
我们观察与之前基本类型的区别,
Func
基本没有区别,Desc
有区别,多了两个函数指针:- void (*copy)
- void (*dispose)
那么继续查看这两个函数:
针对这两个函数,它们的作用就是:
函数 | 作用 | 调用时机 |
__Test__test_block_copy_0 | 调用 _Block_object_assign,相当于retain,将对象赋值在对象类型的结构体变量 __Test__test_block_impl_0中。 | 栈上的Block复制到堆时 |
__Test__test_block_dispose_0 | 调用 _Block_object_dispose,相当于release,释放赋值在对象类型的结构体变量中的对象。 | 堆上Block被废弃时 |
二、内存管理
在我们观察了
Block
对象捕获对象类型的内部结构之后,我们基本就能了解Block
内部是如何管理的:- 在栈上的Block对象复制到堆上,对
person
进行retain
;
- 在堆上Block被废弃时,对
person
进行废弃;
以上操作在
ARC
环境下,由系统帮助我们完成,但在MRC
下,我们仍然要自己管理。下面,我们就一步一步探索验证不同情境下的对象类型内存管理。
2.1 Block在栈上
我们将项目调成
MRC
环境,并在类BFPerson
重写dealloc
测试如下代码:
可以看到,在
[person release]
后,Block
内部再次访问直接崩溃,说明Block
内并没有对person
对象进行强引用,使得person
在内存中释放。总结:
在栈空间,Block不会对auto变量进行强引用。
2.2 Block在堆上
2.1 Block强引用对象类型
我们将2.1,即上节中的代码,在ARC环境下再次试验,打印结果如下:
Block捕获对象类型[94670:4794884] begin Block捕获对象类型[94670:4794884] class: __NSMallocBlock Block捕获对象类型[94670:4794884] age 28 Block捕获对象类型[94670:4794884] end Block捕获对象类型[94670:4794884] BFPerson dealloc
针对以上结果:
ARC
情况下,在Block
赋值给__strong
指针时,栈上的Block
自动拷贝到堆;
- 拷贝的同时会将
person
对象进行retain
操作,在这里相当于强引用;
- 调用
block
时,即使person
出了大括号,系统会release
一次,但由于block
内部仍然强引用person
,所以不会销毁;
- 在打印"end"后,
block
销毁,那么对person
将进行一次release
操作,所以person
对象销毁。
2.2 Block弱引用对象类型
我们将
block
内部引用的auto变量改为__weak
,我们知道__weak
表示的是弱引用指针。上面代码输出的测试结果如下:
Block捕获对象类型[94923:4823185] begin Block捕获对象类型[94923:4823185] class: __NSMallocBlock Block捕获对象类型[94923:4823185] BFPerson dealloc Block捕获对象类型[94923:4823185] age 0 Block捕获对象类型[94923:4823185] end
从输出结果我们很明显的看出,只要出了大括号,
person
对象就被销毁了,此后block
调用,获取到的结果是0,这其实并不是person
对象的age
值。我们将上面代码重写为C++代码,看看Block内部到底发生了什么?
此时发生错误:
针对上面问题,我们指定
ARC
下的运行时系统版本即可:得到的Block对象结构体:
其实上面大体和强引用对象类似,只是其对应
copy
和dispose
函数针对强引用和弱引用有所不同。三、 总结
参考
示例代码