咱们使用的block分三种:静态block(没有使用任何外部变量),栈block(使用外部临时变量),堆block(使用外部成员变量或者属性)。git
静态blockgithub
下面是测试的源码函数
@interface MBlockObj : NSObject @end #import "MBlockObj.h" @implementation MBlockObj - (void)testMBlock { void (^blockM)(void) = ^{ int i = 0; i ++; }; blockM(); } @end
用终端执行 clang -rewrite-objc MBlockObj.m 命令后,我删去多余代码,获得下面这部分测试
struct __MBlockObj__testMBlock_block_impl_0 { struct __block_impl impl; struct __MBlockObj__testMBlock_block_desc_0* Desc; __MBlockObj__testMBlock_block_impl_0(void *fp, struct __MBlockObj__testMBlock_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; static void __MBlockObj__testMBlock_block_func_0(struct __MBlockObj__testMBlock_block_impl_0 *__cself) { int i = 0; i ++; } static struct __MBlockObj__testMBlock_block_desc_0 { size_t reserved; size_t Block_size; } __MBlockObj__testMBlock_block_desc_0_DATA = { 0, sizeof(struct __MBlockObj__testMBlock_block_impl_0)}; static void _I_MBlockObj_testMBlock(MBlockObj * self, SEL _cmd) { void (*blockM)(void) = ((void (*)())&__MBlockObj__testMBlock_block_impl_0((void *)__MBlockObj__testMBlock_block_func_0, &__MBlockObj__testMBlock_block_desc_0_DATA)); ((void (*)(__block_impl *))((__block_impl *)blockM)->FuncPtr)((__block_impl *)blockM); }
能够看到,咱们的 blockM 被变成了atom
((void (*)())&__MBlockObj__testMBlock_block_impl_0((void *)__MBlockObj__testMBlock_block_func_0, &__MBlockObj__testMBlock_block_desc_0_DATA));
__MBlockObj__testMBlock_block_impl_0 须要两个参数, __MBlockObj__testMBlock_block_func_0 主要是对方法的实现内容,第二个参数是对block的一些信息描述。code
附:测试代码中用OC对象测试,结果也是类似。对象
2.栈block作用域
测试代码get
- (void)testMBlock { int i = 0; void (^blockM)(void) = ^{ printf("%d", i); }; blockM(); }
clang出来的代码是这样的cmd
struct __MBlockObj__testMBlock_block_impl_0 { struct __block_impl impl; struct __MBlockObj__testMBlock_block_desc_0* Desc; int i; __MBlockObj__testMBlock_block_impl_0(void *fp, struct __MBlockObj__testMBlock_block_desc_0 *desc, int _i, int flags=0) : i(_i) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; static void __MBlockObj__testMBlock_block_func_0(struct __MBlockObj__testMBlock_block_impl_0 *__cself) { int i = __cself->i; // bound by copy printf("%d", i); } static struct __MBlockObj__testMBlock_block_desc_0 { size_t reserved; size_t Block_size; } __MBlockObj__testMBlock_block_desc_0_DATA = { 0, sizeof(struct __MBlockObj__testMBlock_block_impl_0)}; static void _I_MBlockObj_testMBlock(MBlockObj * self, SEL _cmd) { int i = 0; void (*blockM)(void) = ((void (*)())&__MBlockObj__testMBlock_block_impl_0((void *)__MBlockObj__testMBlock_block_func_0, &__MBlockObj__testMBlock_block_desc_0_DATA, i)); ((void (*)(__block_impl *))((__block_impl *)blockM)->FuncPtr)((__block_impl *)blockM); }
因为访问临时变量 i,因而结构体发生变化,多了一个对应的成员变量,而测试代码执行的任务是打印 i,而 __MBlockObj__testMBlock_block_func_0 中 printf 打印的 i,实际上是作了一次值传递。
附:若是把临时变量换成OC对象,好比NSArray的话,clang出来的代码又有变化
struct __MBlockObj__testMBlock_block_impl_0 { struct __block_impl impl; struct __MBlockObj__testMBlock_block_desc_0* Desc; NSArray *mArr; __MBlockObj__testMBlock_block_impl_0(void *fp, struct __MBlockObj__testMBlock_block_desc_0 *desc, NSArray *_mArr, int flags=0) : mArr(_mArr) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; static void __MBlockObj__testMBlock_block_func_0(struct __MBlockObj__testMBlock_block_impl_0 *__cself) { NSArray *mArr = __cself->mArr; // bound by copy NSLog((NSString *)&__NSConstantStringImpl__var_folders_8x_br8kd9yd70g5q66ssb9jhblh0000gn_T_MBlockObj_6efbe8_mi_0, mArr); } static void __MBlockObj__testMBlock_block_copy_0(struct __MBlockObj__testMBlock_block_impl_0*dst, struct __MBlockObj__testMBlock_block_impl_0*src) { _Block_object_assign((void*)&dst->mArr, (void*)src->mArr, 3/*BLOCK_FIELD_IS_OBJECT*/); } static void __MBlockObj__testMBlock_block_dispose_0(struct __MBlockObj__testMBlock_block_impl_0*src) { _Block_object_dispose((void*)src->mArr, 3/*BLOCK_FIELD_IS_OBJECT*/); } static struct __MBlockObj__testMBlock_block_desc_0 { size_t reserved; size_t Block_size; void (*copy)(struct __MBlockObj__testMBlock_block_impl_0*, struct __MBlockObj__testMBlock_block_impl_0*); void (*dispose)(struct __MBlockObj__testMBlock_block_impl_0*); } __MBlockObj__testMBlock_block_desc_0_DATA = { 0, sizeof(struct __MBlockObj__testMBlock_block_impl_0), __MBlockObj__testMBlock_block_copy_0, __MBlockObj__testMBlock_block_dispose_0}; static void _I_MBlockObj_testMBlock(MBlockObj * self, SEL _cmd) { NSArray* mArr = ((NSArray *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSArray"), sel_registerName("array")); void (*blockM)(void) = ((void (*)())&__MBlockObj__testMBlock_block_impl_0((void *)__MBlockObj__testMBlock_block_func_0, &__MBlockObj__testMBlock_block_desc_0_DATA, mArr, 570425344)); ((void (*)(__block_impl *))((__block_impl *)blockM)->FuncPtr)((__block_impl *)blockM); }
这里主要是多了 __MBlockObj__testMBlock_block_copy_0 和 __MBlockObj__testMBlock_block_dispose_0 两个方法。
前者的方法名了有个 copy ,但不要觉得是执行了 copy 一类的操做。这个方法里面的具体实现,是调用了 _Block_object_assign 方法,_Block_object_assign 方法会根据传进来的实际参数,判断是block对象(flags对应是7),仍是OC对象(flags对应是3),仍是引用(flags对应是8)。若是是OC对象,那么会进行retain,而后再赋值。
后者的方法是一个析构函数做用,但也一样判断实际参数类型,若是是OC对象,会进行release。
3.堆block
测试代码
@interface MBlockObj : NSObject @property (nonatomic, strong) NSArray* mArr; @end #import "MBlockObj.h" @implementation MBlockObj - (void)testMBlock { _mArr = [NSArray array]; void (^blockM)(void) = ^{ NSLog(@"mArr %@", _mArr); }; blockM(); } @end
clang出来的代码
struct __MBlockObj__testMBlock_block_impl_0 { struct __block_impl impl; struct __MBlockObj__testMBlock_block_desc_0* Desc; MBlockObj *self; __MBlockObj__testMBlock_block_impl_0(void *fp, struct __MBlockObj__testMBlock_block_desc_0 *desc, MBlockObj *_self, int flags=0) : self(_self) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; static void __MBlockObj__testMBlock_block_func_0(struct __MBlockObj__testMBlock_block_impl_0 *__cself) { MBlockObj *self = __cself->self; // bound by copy NSLog((NSString *)&__NSConstantStringImpl__var_folders_8x_br8kd9yd70g5q66ssb9jhblh0000gn_T_MBlockObj_58d1bf_mi_0, (*(NSArray **)((char *)self + OBJC_IVAR_$_MBlockObj$_mArr))); } static void __MBlockObj__testMBlock_block_copy_0(struct __MBlockObj__testMBlock_block_impl_0*dst, struct __MBlockObj__testMBlock_block_impl_0*src) { _Block_object_assign((void*)&dst->self, (void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/); } static void __MBlockObj__testMBlock_block_dispose_0(struct __MBlockObj__testMBlock_block_impl_0*src) { _Block_object_dispose((void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/); } static struct __MBlockObj__testMBlock_block_desc_0 { size_t reserved; size_t Block_size; void (*copy)(struct __MBlockObj__testMBlock_block_impl_0*, struct __MBlockObj__testMBlock_block_impl_0*); void (*dispose)(struct __MBlockObj__testMBlock_block_impl_0*); } __MBlockObj__testMBlock_block_desc_0_DATA = { 0, sizeof(struct __MBlockObj__testMBlock_block_impl_0), __MBlockObj__testMBlock_block_copy_0, __MBlockObj__testMBlock_block_dispose_0}; static void _I_MBlockObj_testMBlock(MBlockObj * self, SEL _cmd) { (*(NSArray **)((char *)self + OBJC_IVAR_$_MBlockObj$_mArr)) = ((NSArray *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSArray"), sel_registerName("array")); void (*blockM)(void) = ((void (*)())&__MBlockObj__testMBlock_block_impl_0((void *)__MBlockObj__testMBlock_block_func_0, &__MBlockObj__testMBlock_block_desc_0_DATA, self, 570425344)); ((void (*)(__block_impl *))((__block_impl *)blockM)->FuncPtr)((__block_impl *)blockM); }
好吧,上面的代码看出来了,即便咱们是访问成员变量,也是经由self去获取到成员变量去访问的,而上面的已经解释过, _Block_object_assign 会retain实际参数,就是说会retain self,在某些状况下会形成循环引用。
附1:__block是作了什么操做?
各位能够作测试代码,而后clang一下,能够看到 __block 所修饰的变量,会转成一个结构体,具体的做用,是将这个变量从栈上复制到堆上。
附2:为何类的属性或成员变量能够直接修改,而临时变量须要声明为 __block 才能够修改?
附1的问题的解释里,临时变量在声明为 __block 后,从栈复制到堆上,而类的属性或成员变量,自己就是在堆上,或者能够理解为各自的做用域不一样。
附3:这是block实现过程的源码 https://github.com/mackyle/blocksruntime
附4:block 调用析构函数,释放其所持有的变量的时机?
block 调用析构函数,释放其所持有的变量,是在 block 被设置为 nil,或者被 release 的时候。因此若是是一个临时的block(测试代码中所看到的 block),会在方法调用结束的时候,把block及其持有变量进行释放;而 block 做为属性,除非主动置nil,不然会在其所在类调用 dealloc 的时候释放。
附5:block属性经常使用copy修饰,其实使用strong也是同样的。