在用到block时,我们经常会有这样一种用法
__weak __typeof(self)weakSelf = self;
AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
strongSelf.networkReachabilityStatus = status;
if (strongSelf.networkReachabilityStatusBlock) {//很关键 判空if (strongSelf) 是这段代码的亮点。之所以在Block的代码执行之前加上这样一个判断,就是为了防止在把 weakSelf 转换成 strongSelf 之前 weakSelf 就已经为 nil 了,这样才能确保万无一失。
strongSelf.networkReachabilityStatusBlock(status);
}
};
strong typeof(weakSelf)strongSelf = weakSelf;就是解决这个问题的关键~先将强引用的对象转为弱引用指针,防止了 Block 和对象之间的循环引用。再在 Block 的第一句代码出将 weakSelf 的弱引用转换成 strongSelf 这样的强引用指针,防止了多线程和 ARC 环境下弱引用随时被释放的问题(因为强引用的引用计数至少为1)。
这里大家一定要有一个认识,weakSelf 位于 Block 的外面,strongSelf 位于 Block 的里面。从内存管理的角度来看,weakSelf 是要比 strongSelf 的声明周期要长的。这样就形成了从弱引用到强引用,再从强引用到弱引用的一种变化,也称作 weak-strong dance。
在 block 中先写一个 strong self,其实是为了避免在 block 的执行过程中,突然出现 self 被释放的尴尬情况。通常情况下,如果不这么做的话,还是很容易出现一些奇怪的逻辑,甚至闪退。
其中比较有意思的地方就是先在block外定义一个弱引用的weakself指向self,然后在block内定义一个强引用的self指向weakSelf。那么就针对这两点来讲讲block里weak/strong self的问题。。
weakSelf->self 我们都知道,block比较常见的一个问题就是循环引用问题,简单描述即:self已经持有了block,如果block里再使用self,self将被block截获,然后block持有self,导致循环引用。那么我们在block外定义一个weakSelf指向self,然后block就会截获这个weakSelf,不会再产生循环引用的问题。strongSelf->weakSelf block中还有个有意思的点就是block截获的变量在超出其作用域后仍能使用(比如block截获了self,然后block又被传递到其它地方使用,此时self按理已经释放)。这其实是因为系统会自动的根据情况将block从栈拷贝到堆中并强引用它截获的变量(一般我们的block最开始都是在栈中的),我们知道栈的内存是由系统管理的,而堆是由程序猿管理的,所以实现了变量超出作用域仍能使用。
根据这个说法,我们应该不需要自己强引用weakSelf,我们的weakSelf应该也会被block自己强引用,那我们何必多次一举呢…事实也确实如此,block确实强引用了我们的weakSelf,就算我们不自己强引用weakSelf代码也不会有问题。但是我们在上一段中提到了系统拷贝block是有条件的,有些条件下系统不会自动拷贝block,这种情况下weakSelf超出作用域将被释放。那么哪些情况下系统不会自动copy呢?最常见的一个——block作为参数传递,这也是使用频率非常高的一个点。所以,自己动手是为了更加保险。
下面列出block能够拷贝的情况:
调用block的copy方法
block作为返回值
block赋值时
Cocoa框架中方法名中含有usingBlock的方法
GCD中
*关于GCD中block再提一点: GCD中的block并没有直接或间接被self强引用的,所以不会存在循环引用,故不需要weakSelf;又GCD中block能够自动copy,所以self超出作用域仍可用,故不需要写strongSelf
*总结: weakSelf是为了解决循环引用 strongSelf是为了保证任何情况下self在超出作用域后仍能够使用