1
nathanw 2016-11-09 21:48:08 +08:00 1
object 持有 blk , elk 又通过 block 持有 object 。
相互引用,除非一方置为 nil ,否则无法释放。 |
2
iOran 2016-11-09 23:13:52 +08:00 1
这段代码,建议楼主结合出处<Objective-C 高级编程>来理解,尤其是理解 block 的 C 语言实现。
首先,如 @nathanw 所说, MyObject 的实例 持有 block ; block 中, block 的结构体(block 底层 C 实现是 struct) 又持有 __strong 类型的 self 对象,此时,如果不在 block 执行完之后,将 block 中的 self 设置为 nil ,两者的循环强引用无法打破。 但本段代码也有缺点,如果 execBlock 实例方法不执行,也就是 blk();不执行,实际上没有机会将 id __strong MyObject var 设置为 nil ,也会有循环引用的问题。 合适的解决办法是:将 self 设置为 __weak 类型,防止双方强持有,也就解决问题了。 |
3
jessefang OP |
4
iOran 2016-11-10 10:40:33 +08:00
你确定 “如果这里的 block 获取的不是 self ,而是 self 所持有的属性,不设置为 nil 也可以正常释放” 是正确的吗?
据我所知,这种也是会有循环引用的问题。不信你可以试试如下代码: /////////////////////// 只是换了 MyObject 的定义,其他一样 @interface MyObject : NSObject { blk_t blk_; id obj; } @end @implementation MyObject - (id)init { self = [super init]; blk_ = ^{ NSLog(@"self = %@", obj); }; return self; } - (void)execBlock { blk_(); } @end ////////////////////// 编译器是会报错的: ./YourProj/Test.m:17:24: Capturing 'self' strongly in this block is likely to lead to a retain cycle 上 Xcode 敲一敲试试吧。另外,你也可以用 clang -rewrite-objc Test.m 来查看它的底层 C 实现。 |
5
jessefang OP @iOran
@interface MyObject : NSObject { blk_t blk_; id obj; } @end @implementation MyObject - (id)init { self = [super init]; __block id tmp = obj //用__block 修饰这个属性 blk_ = ^{ NSLog(@"self = %@", tmp); }; return self; } - (void)execBlock { blk_(); } @end /////////////////////////////////////////// 不好意思,我没说清楚,我的情况是以上的代码,可以正常释放 |
6
iOran 2016-11-10 12:28:30 +08:00
总的来说,这和 __block 这个关键字有关系。
这里有种细节可以说明下: 1. id obj; 实际上是:__strong id obj; 这里 obj 会有__strong 修饰符号; 2. 任何有__strong 或者是有__block 或者两种修饰符同时有的变量,在转换成 block 对象时,也就是转换成底层对象时候,会额外生成两个函数,一个叫__main_block_copy_0(它底层会调用_Block_object_assign),另一个叫__main_block_dispose_0(它底层会调用_Block_object_dispose)。这两个函数在栈拷贝 block 入堆 以及 从堆上释放内存块的时候调用。 3. 你用__block 或者__strong 修饰变量,这两种情况,被修饰的对象,都会在函数 block 被生成的时候由栈区入堆区。 4. 拷贝使用 copy ,释放使用 dispose. 最后来看你的 block 的底层实现,我们要关心两部分内容,这是第一部分: /////////////////////////////////////////// 你看到的: blk_ = ^{ NSLog(@"self = %@", tmp); }; /////////////////////////////////////////// 使用 clang -rewrite-objc file.m 转换后的: // @implementation MyObject struct __Block_byref_tmp_0 { void *__isa; __Block_byref_tmp_0 *__forwarding; int __flags; int __size; void (*__Block_byref_id_object_copy)(void*, void*); void (*__Block_byref_id_object_dispose)(void*); id tmp; }; 第二部分: /////////////////////////////////////////// 你看到的: blk_(); /////////////////////////////////////////// 使用 clang -rewrite-objc file.m 转换后的: static void __MyObject__init_block_func_0(struct __MyObject__init_block_impl_0 *__cself) { __Block_byref_tmp_0 *tmp = __cself->tmp; // bound by ref NSLog((NSString *)&__NSConstantStringImpl__var_folders_zg_tjgxj12j793ccbxmtsg3l_0m0000gn_T_Test_b86496_mi_0, (tmp->__forwarding->tmp)); } 看到没,两部分里面的 tmp(第一部分就是 id tmp ,第二部分是 tmp->__forwarding->tmp),和 self 没扯上关系。 |
7
iOran 2016-11-10 12:28:50 +08:00 1
这玩意看代码太痛苦了。
|
9
miketeam 2016-11-23 11:04:04 +08:00
temp ( xxxx66666 ) ---> someInstanceOf "MyObject" xxxx123;
someInstanceOf "MyObject" ---> [- (id)init (xxxxxxxxxxx222222222) have [ temp(&p--->xxxx66666)----> xxxx123] ] so: temp ---> someInstanceOf "MyObject" xxxx123; someInstanceOf "MyObject" xxxx123-----------------> temp ????? temp = nil , or someInstanceOf "MyObject"=nil ??? just temp = nil ; |