1、如下代码,会出现什么问题?
@interface Person : NSObject@property(nonatomic, copy) NSString *name;@property(nonatomic, copy) void(^block)(void);@end@implementation Person- (void)test {self.block = ^{NSLog(@"%@",_name);};}@end
解答:会出现循环引用问题,并且xcode会触发2条编译警告⚠️
Block implicitly retains 'self'; explicitly mention 'self' to indicate this is intended behaviorCapturing 'self' strongly in this block is likely to lead to a retain cycle
对成员变量在block中直接访问,造成block->Person的强引用,Person->block的强引用,最终造成循环引用。
修改代码:
- (void)test {self.block = ^{NSLog(@"%@",self->_name);};}
这种情况下,会造成循环引用,Xcode编译器会发出⚠️
Capturing 'self' strongly in this block is likely to lead to a retain cycle
继续修改代码
- (void)test {Person *p = self;self.block = ^{NSLog(@"%@",p->_name);//NSLog(@"%@",p.name);};}
这种情况下,会造成循环引用问题,编译器不会警告
继续修改代码
- (void)test {__block Person *p = self;self.block = ^{NSLog(@"%@",p.name);};}
这种情况下,依然会造成循环引用问题
继续修改代码
- (void)test {Person *p = [Person new];p.block = ^{NSLog(@"%@",_name);};}
这种情况下,不会造成循环引用问题,但Xcode会发出编译器警告⚠️
Block implicitly retains 'self'; explicitly mention 'self' to indicate this is intended behavior
继续修改代码
- (void)test {__weak Person *p = self;self.block = ^{NSLog(@"%@",p.name);};}
这种情况下,不会造成循环引用。dealloc正常被调用
继续修改代码
- (void)test {__unsafe_unretained Person *p = self;self.block = ^{NSLog(@"%@",p.name);};}
这种情况下,不会造成循环引用。
问题1总结:
- block里面使用self并不一定都会造成循环引用
- self强引用了block,则block中使用self或者成员变量都会造成循环引用
- block不能解决循环引用,weak和__unsafe_unretained能解决循环引用问题
2、weak和strong,为什么需要使用__strong
__weak __typeof(self)weakSelf = self; //1[self.context performBlock:^{[weakSelf doSomething]; //2__strong __typeof(weakSelf)strongSelf = weakSelf; //3[strongSelf doSomething];}]
- 使用weak typeof是在编译的时候,另外创建一个weak对象来操作self。
- 因为weakSelf和self是两个内容,doSomething有可能就直接对self自身引用计数减到0了. 所以在[weakSelf doSomething]的时候,你很难控制这里self是否就会被释放了.weakSelf只能看着。
- strong typeof在编译的时候,实际是对weakSelf的强引用。指针连带关系self的引用计数还会增加.但是你这个是在block里面,生命周期也只在当前block的作用域。所以,当这个block结束, strongSelf随之也就被释放了.同时也不会影响block外部的self的生命周期。
3、Block对变量的捕获
如下代码,打印的结果是多少?
- (void)test2 {NSInteger a = 3; // 局部变量NSInteger(^block)(NSInteger) = ^NSInteger(NSInteger n){return n * a;};a = 1;printf("%zd \n",block(2));}
打印结果:
打印的结果是6,Block对局部变量的截获并没有因为a重新赋值而发生变化。
继续修改代码
- (void)test2 {static NSInteger a = 3; //局部静态变量NSInteger(^block)(NSInteger) = ^NSInteger(NSInteger n){return n * a;};a = 1;printf("%zd \n",block(2));}
打印结果
打印的结果是2,a发生变化之后block内部的a也发生了改变。
继续修改代码
NSInteger a = 3; //全局变量- (void)test2 {NSInteger(^block)(NSInteger) = ^NSInteger(NSInteger n){return n * a;};a = 1;printf("%zd \n",block(2));}
打印结果
从打印结果发现,全局变量a修改之后影响到了Block内部的a发生变化
继续修改代码
static NSInteger a = 3;- (void)test2 {NSInteger(^block)(NSInteger) = ^NSInteger(NSInteger n){return n * a;};a = 1;printf("%zd \n",block(2));}
打印结果
打印输出是2,全局静态变量a在修改之后也直接影响到了block中的值
继续修改代码
- (void)test2 {__block NSInteger a = 3;NSInteger(^block)(NSInteger) = ^NSInteger(NSInteger n){return n * a;};a = 1;printf("%zd \n",block(2));}
打印结果
打印输出是2,说明__block修饰的局部变量在Block内部是指针捕获
继续修改代码
__block NSInteger a = 3;- (void)test2 {NSInteger(^block)(NSInteger) = ^NSInteger(NSInteger n){return n * a;};a = 1;printf("%zd \n",block(2));}
编译报错,提示❌
__block attribute not allowed, only allowed on local variables
**
使用clang查看局部变量,全局变量,局部静态变量,全局静态变量,__block变量的底层代码结构
源代码:
NSInteger n1 = 3;static NSInteger n2 = 3;- (void)test3 {NSInteger n3 = 3;static NSInteger n4 = 3;__block NSInteger n5 = 3;void(^block)(void) = ^void(){NSLog(@"%zd",n1);NSLog(@"%zd",n2);NSLog(@"%zd",n3);NSLog(@"%zd",n4);NSLog(@"%zd",n5);};block();}
clang编译后的C++代码
NSInteger n1 = 3;static NSInteger n2 = 3;struct __Block_byref_n5_1 {void *__isa;__Block_byref_n5_1 *__forwarding;int __flags;int __size;NSInteger n5;};struct __Person__test3_block_impl_0 {struct __block_impl impl;struct __Person__test3_block_desc_0* Desc;NSInteger n3;NSInteger *n4;__Block_byref_n5_1 *n5; // by ref__Person__test3_block_impl_0(void *fp, struct __Person__test3_block_desc_0 *desc, NSInteger _n3, NSInteger *_n4, __Block_byref_n5_1 *_n5, int flags=0) : n3(_n3), n4(_n4), n5(_n5->__forwarding) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;}};static void __Person__test3_block_func_0(struct __Person__test3_block_impl_0 *__cself) {__Block_byref_n5_1 *n5 = __cself->n5; // bound by refNSInteger n3 = __cself->n3; // bound by copyNSInteger *n4 = __cself->n4; // bound by copyNSLog((NSString *)&__NSConstantStringImpl__var_folders_5m_900ng1910q18rfy4jrhcbdm40000gn_T_Person_ef54cb_mi_1,n1);NSLog((NSString *)&__NSConstantStringImpl__var_folders_5m_900ng1910q18rfy4jrhcbdm40000gn_T_Person_ef54cb_mi_2,n2);NSLog((NSString *)&__NSConstantStringImpl__var_folders_5m_900ng1910q18rfy4jrhcbdm40000gn_T_Person_ef54cb_mi_3,n3);NSLog((NSString *)&__NSConstantStringImpl__var_folders_5m_900ng1910q18rfy4jrhcbdm40000gn_T_Person_ef54cb_mi_4,(*n4));NSLog((NSString *)&__NSConstantStringImpl__var_folders_5m_900ng1910q18rfy4jrhcbdm40000gn_T_Person_ef54cb_mi_5,(n5->__forwarding->n5));}static void __Person__test3_block_copy_0(struct __Person__test3_block_impl_0*dst, struct __Person__test3_block_impl_0*src) {_Block_object_assign((void*)&dst->n5, (void*)src->n5, 8/*BLOCK_FIELD_IS_BYREF*/);}static void __Person__test3_block_dispose_0(struct __Person__test3_block_impl_0*src) {_Block_object_dispose((void*)src->n5, 8/*BLOCK_FIELD_IS_BYREF*/);}static struct __Person__test3_block_desc_0 {size_t reserved;size_t Block_size;void (*copy)(struct __Person__test3_block_impl_0*, struct __Person__test3_block_impl_0*);void (*dispose)(struct __Person__test3_block_impl_0*);} __Person__test3_block_desc_0_DATA = { 0, sizeof(struct __Person__test3_block_impl_0), __Person__test3_block_copy_0, __Person__test3_block_dispose_0};static void _I_Person_test3(Person * self, SEL _cmd) {NSInteger n3 = 3;static NSInteger n4 = 3;__attribute__((__blocks__(byref))) __Block_byref_n5_1 n5 = {(void*)0,(__Block_byref_n5_1 *)&n5, 0, sizeof(__Block_byref_n5_1), 3};void(*block)(void) = ((void (*)())&__Person__test3_block_impl_0((void *)__Person__test3_block_func_0, &__Person__test3_block_desc_0_DATA, n3, &n4, (__Block_byref_n5_1 *)&n5, 570425344));((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);}
分析以上C++代码,可以知道,Block对全局变量,静态全局变量是直接使用,在Block内部没有做任何操作,所以Block没有对全局变量和静态全局变量进行捕获。
从__Person__test3_block_impl_0内部可以看出block转化为结构体对象之后,会自动生成对应的属性 NSInteger n3;``NSInteger *n4;``__Block_byref_n5_1 *n5;,block修饰的局部变量也会生成一个结构体Block_byref_n5_1。可以看出NSInteger *n4;__Block_byref_n5_1 *n5;是指针类型。
再继续分析Persontest3_block_func_0可以看出,这里进行了一次赋值操作
__Block_byref_n5_1 *n5 = __cself->n5; // bound by refNSInteger n3 = __cself->n3; // bound by copyNSInteger *n4 = __cself->n4; // bound by copy
可以看出是对生成的结构体属性进行赋值,(n5->__forwarding->n5)这里可以看出block修饰的变量生成的结构体对象中`forwarding`指针指向了变量本身
总结:
