block即代码块,和其他属性类似,只是block存储的是函数体。block执行的代码,是在编译时已经生成好的了。首先来看一下block的内存模型:
struct Block_descriptor { unsigned long int reserved; unsigned long int size; void (*copy)(void *dst, void *src); void (*dispose)(void*); }; struct Block_layout { void *isa; int flags; int reserved; void (*invoke)(void *, ...); struct Block_descriptor *descriptor; /* Imported variables */ };
- isa 指针, 所有对象都有改指针,用于实现对象相关的功能。
- flags,用于按bit位表示一下block的附加信息。
- reserved,保留变量。
- invoke,函数指针,指向具体的block实现的函数调用地址。
- descriptor,表示该block的附加描述信息,主要是size大小,以及copy和dispose函数指针。
- variables,capture过来的变量,block能够访问它外部的局部变量,就是因为将这些变量(活变量的地址)复制到了结构体中。
block使用局部变量的时候,可以访问,但是不可以修改变量的值。如果是访问指针类型,会修改指针所指地址的内容,不会修改指针。如果需要在block内部对局部变量做修改,使用关键字__block。被__block修饰的变量称作block变量。基本类型的block变量等效于全局变量或静态变量。
关于block的循环引用的问题:
循环引用是指两个对象互相强引用对方,即retain了对方,从而导致两个对象都无法释放而导致内存泄漏。block中的循环引用问题,是因为block在考呗到堆上的时候,会retain其引用的外部变量,那么如果block中引用了他的宿主对象,那很有可能引起循环引用。而解决的办法就是使用__weak或者__unsafe_unretained来修饰,例如
1 typedef void(^MyBlock)(void); 2 @interface MyViewController : UIViewController 3 4 @property(nonatomic, assign)MyBlock myBlock; 5 6 @end 7 8 9 10 @implementation MyViewController 11 12 - (void)viewDidLoad{ 13 [super viewDidLoad]; 14 15 //系统警告:capturing ‘self‘ strongly in this block is likely to lead to retain cycle,直接导致循环引用 16 self.myBlock = ^{ 17 [self func]; 18 }; 19 20 //不会引起循环引用 21 __weak typeof(self) weakSelf = self; 22 self.myBlock = ^{ 23 [weakSelf doSth]; 24 }; 25 self.myBlock(); 26 } 27 28 - (void)func{ 29 NSLog(@"doSomething"); 30 } 31 32 @end
但是在不会引起循环引用的block实现中,会存在一个问题,这个得从__weak说起。
弱引用为什么不引起循环引用?就是因为弱引用是不加引用计数,这样会产生一定的风险,其引用的对象随时有可能被ARC释放掉。为了解决这个问题,而引入的一个解决方法是:weak-strong dance。代码如下
__weak typeof(self) weakSelf = self; self.block = ^{ //加入强引用 __strong typeof(weakSelf) strongSelf = weakSelf; [strongSelf doSth]; };
这样加入强引用,可以避免self在执行block的时候被析构而无法执行。
现在再来看一个问题,如果在添加强引用之前weakSelf已经为nil了,那强引用出来也是nil,再去执行方法同样会出问题,那为了保证方法能正确执行,Apple官方文档建议为:
__weak typeof(self) weakSelf = self; self.block = ^{ __strong typeof(weakSelf) strongSelf = weakSelf; //如果强引用self不为空才执行 if (strongSelf) { [strongSelf doSth]; } };
这就是weak-strong dance的合理做法。
关于__unsafe_unretained, 文档中是这么写的:
In some cases you can use __unsafe_unretained if the class isn’t __weak compatible. This can, however, become impractical for nontrivial cycles because it can be hard or impossible to validate that the __unsafe_unretained pointer is still valid and still points to the same object in question.
意思是:在某些情况下,如果类不支持__weak,可以使用__unsafe_unretained,然而它在实际中仍然无法解决non-trivial cycles问题,因为它很难甚至不可能来保持__unsafe_unretained指针活跃并且仍然指向同一个对象。
官方文档传送门: