如果在 Block 内部使用外部的变量,Block 会持有这个变量。下面来看几中特殊的情况,看 Block 对变量的持有情况如何。
typeof
@interface X : NSObject @property (nonatomic, copy) void(^testBlock1)(void); @property (nonatomic, copy) void(^testBlock2)(void); @property (nonatomic, copy) NSString *str; @end @implementation X - (void)test { self.testBlock1 = ^{ typeof (self) x; }; } @end
上面代码中, test 方法里面设置了 Block,并且 Block 里面有一句 typeof (self) x,那么在这种情况下,Block 会持有 self 吗?
使用 clang -rewrite-objc 命令,查看 c++ 代码,代码如下:
struct __X__test_block_impl_0 { // Block实现,没有持有 self struct __block_impl impl; struct __X__test_block_desc_0* Desc; __X__test_block_impl_0(void *fp, struct __X__test_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; static void __X__test_block_func_0(struct __X__test_block_impl_0 *__cself) { // Block方法实现 X * x; // typeof (self) x; }
从代码可以看到,typeof (self) x 被替换成了 X *x,因此这种情况下 Block 不会持有 self。
sizeof
将 test 方法修改如下:
- (void)test { self.testBlock1 = ^{ size_t i = sizeof (self); }; }
在上面的方法中,Block 内部有使用 sizeof (self),那么 Block 会持有 self 吗?
同样查看 c++ 代码,代码如下:
struct __X__test_block_impl_0 { // Block实现 struct __block_impl impl; struct __X__test_block_desc_0* Desc; X *self; // 持有了 self __X__test_block_impl_0(void *fp, struct __X__test_block_desc_0 *desc, X *_self, int flags=0) : self(_self) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; static void __X__test_block_func_0(struct __X__test_block_impl_0 *__cself) { // Block方法 X *self = __cself->self; // bound by copy size_t i = sizeof (self); }
可以看到,在使用 sizeof 时,Block 是持有了 self的。
是否会持有属性
将 test 方法修改如下:
- (void)test { self.testBlock1 = ^{ self.str = @"KK"; // 设置属性 }; } @end
上面代码中,Block 除了会持有 self,还会持有属性 str 吗?
查看 c++ 代码,代码如下:
struct __X__test_block_impl_0 { // Block实现 struct __block_impl impl; struct __X__test_block_desc_0* Desc; X *self; // 只持有了self,未持有属性str __X__test_block_impl_0(void *fp, struct __X__test_block_desc_0 *desc, X *_self, int flags=0) : self(_self) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; static void __X__test_block_func_0(struct __X__test_block_impl_0 *__cself) { // Block方法实现 X *self = __cself->self; // bound by copy ((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)self, sel_registerName("setStr:"), (NSString *)&__NSConstantStringImpl__var_folders_p6_jy49zvqx2656qb8rbq64hc_w0000gn_T_variable_a3a045_mi_0); // 调用设置属性方法 }
可以看到,Block 没有持有属性 str。
是否会持有实例变量
将 test 方法修改如下:
- (void)test { self.testBlock1 = ^{ _str = @"KK"; // 调用实例变量 }; }
此时,Block 会持有实例变量吗?
查看 c++ 代码,代码如下:
struct __X__test_block_impl_0 { // Block实现 struct __block_impl impl; struct __X__test_block_desc_0* Desc; X *self; // 只持有self,未持有实例变量 __X__test_block_impl_0(void *fp, struct __X__test_block_desc_0 *desc, X *_self, int flags=0) : self(_self) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; static void __X__test_block_func_0(struct __X__test_block_impl_0 *__cself) { // Block方法实现 X *self = __cself->self; // bound by copy (*(NSString **)((char *)self + OBJC_IVAR_$_X$_str)) = (NSString *)&__NSConstantStringImpl__var_folders_p6_jy49zvqx2656qb8rbq64hc_w0000gn_T_variable_d40340_mi_0; // 使用偏移设置实例变量 }
可以看到,Block 没有持有实例变量。
嵌套Block
将 test 方法修改如下:
- (void)test { int i = 1; self.testBlock1 = ^{ self.testBlock2 = ^{ // 嵌套Block self.str = @"KK"; int j = i; }; }; }
上面代码中使用了嵌套 Block,内层Block使用了变量 i,而外层 Block 没有使用变量 i,外层 Block 是否会持有变量 i 呢?
查看 c++ 代码,代码如下:
struct __X__test_block_impl_0 { // 内层Block实现 struct __block_impl impl; struct __X__test_block_desc_0* Desc; X *self; // 持有了self int i; // 持有了变量i __X__test_block_impl_0(void *fp, struct __X__test_block_desc_0 *desc, X *_self, int _i, int flags=0) : self(_self), i(_i) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; static void __X__test_block_func_0(struct __X__test_block_impl_0 *__cself) { // 内层Block 方法实现 X *self = __cself->self; // bound by copy int i = __cself->i; // bound by copy ((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)self, sel_registerName("setStr:"), (NSString *)&__NSConstantStringImpl__var_folders_p6_jy49zvqx2656qb8rbq64hc_w0000gn_T_variable_926b0d_mi_0); int j = i; } struct __X__test_block_impl_1 { // 外层Block实现 struct __block_impl impl; struct __X__test_block_desc_1* Desc; X *self; // 持有了self __X__test_block_impl_1(void *fp, struct __X__test_block_desc_1 *desc, X *_self, int flags=0) : self(_self) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; static void __X__test_block_func_1(struct __X__test_block_impl_1 *__cself) { // 外层Block方法实现 X *self = __cself->self; // bound by copy ((void (*)(id, SEL, void (*)()))(void *)objc_msgSend)((id)self, sel_registerName("setTestBlock2:"), ((void (*)())&__X__test_block_impl_0((void *)__X__test_block_func_0, &__X__test_block_desc_0_DATA, self, i, 570425344))); }
从代码上看,内层 Block 持有 self 和变量 i,外层 Block 只是持有了 self,并未持有变量 i。