想研究什么是循环引用,就要动手做实验,实验的结果如何验证?也就是如何判断一个对象循环引用了?
验证方法
最简单的办法就是在 dealloc
方法中打印 log,判断一个对象的 dealloc
方法是否调用,如果调用了表示被释放了。再注意要排除干扰,只设置实验对象的引用,不要设置多余的引用会影响计数。
- (void)dealloc() {
NSLog(@"%s", __func__);
}
什么是循环引用
循环引用的概念很简单,对象之间的强引用链形成了环就创造了一个循环引用。
最简单的情况,两个对象,你引用我,我引用你,就形成了循环引用。再复杂一点不过是多个对象构成一个更大的引用环。
需要注意的是 block 的使用造成的强引用链,block 会捕获内部使用的对象,形成隐式的强引用,一般有以下两种常见的情况:
- 引用 self:直接写 self。
self.callback = ^{
NSLog(@"callback: %@", self);
}
- 成员变量:不写 self,但实际上还是对 self 的强引用
self.callback = ^{
NSLog(@"callback: %@", _name);
// 等价于
NSLog(@"callback: %@", self->_name);
}
使用 weak 解除强引用
核心方法只有一个,就是用 weak 引用解除 strong 引用。各种方案的不同之处在于什么时候定义 weak 应用。
直接定义属性为 weak 类型:
@property (weak, nonatomic) id<RadarNodeDelegate> delegate;
让 block 捕获 weak 引用替代捕获 strong 引用:
__weak typeof(self) weakSelf = self;
self.callback = ^() {
NSLog(@"callback: %@", weakSelf.name);
}
由于弱引用指向的对象在使用的时候可能已经被回收了,被回收后弱引用会自动指向 nil。
__weak typeof(self) weakSelf = self;
self.callback = ^() {
NSLog(@"callback: %@", weakSelf ? weakSelf._name : @"???");
}
上面这段代码是有一些安全隐患的,因为 weakSelf
被使用了两次,在两次使用之间,weakSelf
指向的对象可能会释放,会导致不一致的情况。
不过这也不是肯定会发生的,还要看执行 callback
的线程和 执行 dealloc
的线程是否相同,如果相同肯定不会发生这种情况,要么在执行 callback
的时候 weakSelf
已经指向了 nil
,要么在执行 callback
的时候 weakSelf
不会被释放。
但 dealloc
执行的线程并不一定是主线程,或者某个固定的线程,而是最后一个由自动引用计数释放它的线程。在多个线程中使用一个对象肯定会产生这种隐患。
因此另一个方法用来避免这种情况:
__weak typeof(self) weakSelf = self;
self.callback = ^() {
__strong typeof(self) strongSelf = weakSelf;
NSLog(@"callback: %@", strongSelf ? strongSelf.name : @"???");
}
使用 __strong
关键字将弱引用转换为强引用,这个操作是原子操作,结果要么是 nil
要么指向了对象。一旦指向了对象,由于是强引用,就保证了对象不会被释放掉,又由于是局部引用,一旦退出作用域,如果只剩下这个引用就会释放引用。既保证了使用过程中不会突然变成 nil
,也保证在执行结束后正确释放掉。
有的文章说 strong 化引用的目的是防止被释放,这个说法是片面的,如果在 strong 化的代码之前,也就是进入 block 之前就被释放了,那么 strong 引用的值也是 nil。它的作用在于解决 block 内部使用多次弱引用对象有可能不一致的问题,而不是保证该对象在执行 block 之前不被释放。
(ole)
网友评论