在使用 Block
时,除了使用 __weak
修饰符避免循环引用外,还有一点常常容易忘记。苹果把它称为:“Strong-Weak Dance”。git
这是一种 强引用 --> 弱引用 --> 强引用 的变换过程。在弄明白为何要如此大费周章以前,咱们首先来看看通常的写法会有什么问题。github
__weak MyViewController *wself = self;
self.completionHandler = ^(NSInteger result) {
[wself.property removeObserver: wself forKeyPath:@"pathName"];
};
复制代码
这种写法能够避免循环引用,可是咱们要考虑这样的问题:swift
假设 block
被放在子线程中执行,并且执行过程当中 self
在主线程被释放了。因为 wself
是一个弱引用,所以会自动变为 nil
。而在 KVO 中,这会致使崩溃。多线程
解决以上问题的方法很简单,新增一行代码便可:闭包
__weak MyViewController *wself = self;
self.completionHandler = ^(NSInteger result) {
__strong __typeof(wself) sself = wself; // 强引用一次
[sself.property removeObserver: sself forKeyPath:@"pathName"];
};
复制代码
这样一来,self
所指向对象的引用计数变成 2,即便主线程中的 self
由于超出做用于而释放,对象的引用计数依然为 1,避免了对象的销毁。app
在和小伙伴的讨论过程当中,他提出了几个问题。虽然都不难,可是有利于把各类知识融会贯通起来。函数
__strong __typeof(wself) sself = wself;
复制代码
A:会的。引用计数描述的是对象而不是指针。这句话的意思是:ui
sself 强引用 wself 指向的那个对象spa
所以对象的引用计数会增长一个。线程
block
内部定义了sself
,会不会所以强引用了 sself
?A:不会。block
只有截获外部变量时,才会引用它。若是是内部新建一个,则没有任何问题。
block
内部没有强引用,而是经过 if
判断,是否是也能够,好比这样写:__weak MyViewController *wself = self;
wself.completionHandler = ^(NSInteger result) {
if (wself) { // 只有当 wself 不为 nil 时,才执行如下代码
[wself.property removeObserver: wself forKeyPath:@"pathName"];
}
};
复制代码
A:不能够!考虑到多线程执行,也许在判断的时候,self
还没释放,可是执行 self
里面的代码时,就恰好释放了。
block
内部强引用也没用啊。也许 block
执行之前,self
就释放了。A:有用!若是在 block
执行之前,self
就释放了,那么 block
的引用计数降为 0,因此本身就会被释放。这样它根本就不会被执行。另外,若是执行一个为 nil
的闭包会致使崩溃。
block
的过程当中,block
被释放了怎么办?A:简单来讲,block
还会继续执行,可是它捕获的指针会具备不肯定的值,详细内容请参考这篇文章
这是 ReactiveCocoa 中定义的一个宏。通常能够这样使用:
@weakify(self);
self.completionHandler = ^(NSInteger result) {
@strongify(self);
[self.property removeObserver: sself forKeyPath:@"pathName"];
};
复制代码
本文并不是分析它们的实现原理,因此就简单解释两点:
这里的“@”没有任何用处,仅表示强调,这个宏实际上包含了一个空的 AutoreleasePool
,这也就是为何必定要加上“@”。
它的原理仍是和以前同样,生成了一段形如 __weak MyViewController *wself = self;
这种格式的代码:
#define rac_strongify_(INDEX, VAR) \\
__strong __typeof__(VAR) VAR = metamacro_concat(VAR, _weak_);
复制代码
感谢 @Cyrus_dev 的提醒,在 Swift 中也有 Strong-Weak Dance 的概念。最简单的方法就是直接沿用 OC 的思路:
self.completionHandler = { [weak self] in
if let strongSelf = self {
/// ....
}
};
复制代码
这种写法的缺点在于,咱们不能写 if let self = self
,所以须要从新定义一个变量 strongSelf
,命名方式显得不够优雅。
除此之外还可使用 Swift 标准库提供的函数 withExtendedLifetime
:
self.completionHandler = { [weak self] in
withExtendedLifetime(self) {
/// ...
}
};
复制代码
这种写法的缺点在于,self
依然是可选类型的,还须要把它解封后才能使用。
最后,还有一种解决方案是自定义 withExtendedLifetime
函数:
extension Optional {
func withExtendedLifetime(body: T -> Void) {
if let strongSelf = self {
body(strongSelf)
}
}
}
复制代码
至于这种写法是否更加优雅,就见仁见智了:
self.completionHandler = { [weak self] in
self.withExtendedLifetime {
/// 这里用 $0 表示 self
}
};
复制代码