循环引用问题 (Reference Cycle)
引用计数这种管理内存的方式虽然简单,但是有一个比较大的瑕疵,它不能很好的解决循环引用问题
什么是循环引用问题?
对象A和对象B,相互引用了对方作为自己的成员变量,只有当自己销毁时,才会将成员变量的引用计数减1,这就导致了A的销毁依赖于B的销毁,同样B的销毁依赖于A的销毁,这样就造成了循环引用问题。
1.使用弱引用
主动断开循环引用需要程序员能够准确发现循环引用,并知道什么时机断开循环引用,所以这种解决方法并不常见,更常见的办法是使用弱引用
弱引用虽然持有对象,但是不增加引用计数,这样就避免了循环引用的产生。例如delegate模式中的weak声明。View Controller的delegate成员变量通常是一个弱引用,以避免两个View Controller互相引用对方造成循环引用。
弱引用的实现原理
系统对于每一个有弱引用的对象,都维护一个表来记录它所有的弱引用的指针地址。当一个对象的引用计数为0时,系统就通过这张表,找到所有的弱引用指针,将他们置为nil。弱引用的使用是有额外的开销的,虽然这个开销很小,但是如果肯定不需要使用弱引用特性时,就不应该盲目使用弱引用
通过weakSelf和strongSelf解决循环引用
__weak __typeof (self)weakSelf = self;
_observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"Change" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
if (strongSelf) {
[strongSelf reload];
}
}
1.在block之前定义对self的弱引用weakSelf,因为是弱引用,所以self被释放时weakSelf会变成nil
2.在block中引用该弱引用,考虑到多线程情况,通过强引用strongSelf来引用该弱引用,如果self不为nil,就会retain self,以防在block内部使用过程中self被释放
3.在block块中使用该强引用strongSelf,注意对strongSelf进行nil检测,因为多线程在弱引用weakSelf对强引用strongSelf赋值时,弱引用weakSelf可能已经为nil了
4.强引用strongSelf在block作用域结束之后,自动释放
weakSelf为什么需要strongSelf配合使用
在block块中先写一个strongSelf,是为了避免在block的执行过程中,突然出现self被释放的情况。
比如以下情况可能就会出现weakself被释放
__weak __typeof__(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[weakSelf doSomething];
[weakSelf doOtherThing];
});
解决方法
__weak __typeof__(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
__strong __typeof(self) strongSelf = weakSelf;
[strongSelf doSomething];
[strongSelf doOtherThing];
});
在 doSomething 中,weakSelf 不会变成 nil,不过在 doSomething 执行完成,调用第二个方法 doOtherThing 的时候,weakSelf 有可能被释放,于是,strongSelf 就派上用场了
总结
1 在 Block 内如果需要访问 self 的方法、变量,建议使用 weakSelf。
2 如果在 Block 内需要多次 访问 self,则需要使用 strongSelf。