什么是 Weak-Strong Dance ?
在使用的 Block
时, 除了使用 __weak
修饰符去避免循环引用外,还可以通过名为 Weak-Strong Dance
的方式去避免循环引用。 其实 Weak-Strong Dance
并不是一个新东西,它只是 __weak
的一个升级版本。主要目的是为了避免在极端情况下 __weak
这种情况会出现的问题。
使用 __weak 形式避免循环引用有什么问题?
typedef void(^blk_t)();
@interface TObject : NSObject
@property (nonatomic) blk_t block;
@end
@implementation TObject
- (instancetype)init {
self = [super init];
__weak typeof(self) weakSelf = self;
self.block = ^() {
NSLog(@"block start...");
NSLog(@"self is %@", weakSelf);
NSLog(@"block end...");
};
return self;
}
@end
上面这段代码在大部分的情况下都是可行的,Block 捕获 weakSelf。当 self 被释放的时候, weakSelf 也会被设为 nil
。self 持有的 Block 当然也会被设为 nil
。但是在多线程的情况当 block 执行到 NSLog(@"block start...")
时, 在另一个线程中此时 self 被释放。 weakSelf 也被设为 nil
。那么这个例子中就会打印出 self is nil
。这没出现啥问题。 但是如果是移除 KVO 的观察者,或者添加到 NSDictionary 中这样的 API。值为 nil
就会造成程序 carsh。
通过 Weak-Strong Dance 安全的避免循环引用
- (instancetype)init {
self = [super init];
__weak typeof(self) weakSelf = self;
self.block = ^() {
__strong typeof(weakSelf) strongSelf = weakSelf;
NSLog(@"%@", strongSelf);
};
return self;
}
通过对上面的代码增加了一句 __strong typeof(weakSelf) strongSelf = weakSelf;
来避免了上面说的极端情况,这就是 Weak-Strong Dance
。 这句话的作用在于一执行 Block 的时候,就利用一个强引用去持有 weakSelf。 这样在 Block 执行的过程中就不用担心 self 会被释放了, 因为此时有个强引用持有他。这也不会造成循环引用,因为 Block 执行时 strongSelf
才会指向 self,Block 执行完成后 strongSelf
就随着超出作用域而被系统回收了。
拓展&加深
在 @kuailejim 的文章 Weak-Strong-Dance真的安全吗?》 看到个新的观点: 当刚进入 Block 时还没有为 strongSelf 赋值时,此时 weakSelf 被设置成 nil
,那 strongSelf 的值还是 nil
。 还是不够安全,所以在对 strongSelf 赋值后,再做一次非空判断。这样就绝对没毛病了:
__weak typeof(self) weakSelf = self;
self.block = ^() {
__strong typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf == nil) {return;};
NSLog(@"%@", strongSelf);
};