Blocks篇:6.Blocks捕获的OC对象及其内存管理
之前所说的,都是Block对象捕获基本数据类型变量时的处理方式。现在我们看一下对于OC对象被Block捕获时的情况。
1.Block对象捕获OC对象
|
|
此代码运行正常无误。这也直接证明了Block对象对该数组对象进行了强引用,使被捕获对象的生存周期超过了原始作用域。究其原因,查看部分转换后的代码:
|
|
在转换代码中显而易见,Block将捕获的OC对象以强引用的方式(ARC下隐含为__strong),作为结构体的成员变量。并在Block的描述信息对象中提供了copy和dispose方法,在适当时机调用以配合内存管理。
注意:
- 由于编译器支持Block结构的类引用计数的内存管理方式,即ARC下,系统可以在适当时机分配和释放Block对象的内存。故在Block的相关结构体中可以直接存入带有所有权修饰符的OC对象。
- 由于OC对象本身分配在堆内存中,Block对象只需通过指针传递即可完成捕获。
- 由于捕获后,Block对象也参与了OC对象的内存管理,故需要提供保留和释放相关函数,以备系统在堆内存中创建(copy操作)和释放Block对象(dispose操作)时进行调用。这里则与__block变量时一样,使用了_Block_object_assign和_Block_object_dispose函数对OC对象进行保留和释放操作。
补充:
在_Block_object_assign以及_Block_object_dispose中,用于区分捕获变量类型的标识:
捕获的对象 | 标识值 |
---|---|
OC对象 | BLOCK_FIELD_IS_OBJECT |
__block修饰的变量或对象 | BLOCK_FIELD_IS_BYREF |
2.__block修饰的OC对象
2.1 block修饰的强引用对象(strong所有权)
还是先看实例:
|
|
可以看到,当Block执行后,在外部读取捕获的OC对象,可以发现仍然指向同一地址。这与__block修饰基本变量的情况一致,也是需要通过生成特定捕获变量的结构体实例,在拷贝到堆内存后,最终访问均指向相同的对象。
|
|
- 注意,这里相比较普通的block变量时,**在block变量结构体中,新增了对于__block变量在堆内存中进行分配和释放时的函数指针成员**:
|
|
这是由于__block修饰的是OC对象,其结构体实例赋值此OC对象的内存管理,故需要提供保留和释放操作。
- 同样的,在栈中(声明block对象所在的地方)或是堆中(Block对象执行的函数体中),都可以通过forwarding指针访问到相同的OC对象:
|
|
2.2 __block修饰的其他OC对象
- 当__block修饰的OC对象为弱对象时,我们以最开始的例子进行修改验证:
|
|
执行情况为:
个数:0
个数:0
个数:0
Program ended with exit code: 0
在block对象中保留的是数组的弱引用,当原数组对象释放后,block对象中的OC对象自动置为nil,故结果为0。
- 当block修饰的OC对象为unsafe_unretained修饰的对象时,与弱引用情况相同,只是需要保证不要访问野指针。
- block修饰的OC对象不允许标记为autoreleasing。