面试题答案
一键面试循环引用问题
在这种情况下,当completionHandler
被持有(比如传递给其他对象并在之后调用),由于completionHandler
内部使用了self
,self
就会持有completionHandler
,而completionHandler
又持有self
,从而形成循环引用。这会导致ViewController
及其相关资源在其生命周期结束时无法被释放,造成内存泄漏。
解决方法及原理
- 使用__weak关键字
- 方法:在
ViewController
内部,声明一个__weak
类型的变量指向self
,然后在completionHandler
中使用这个弱引用变量。 - 原理:
__weak
修饰的变量不会增加对象的引用计数。这样在completionHandler
持有这个弱引用变量时,不会导致循环引用。当self
的引用计数降为0被释放时,这个弱引用变量会自动被设置为nil
,避免了野指针问题。
__weak typeof(self) weakSelf = self; self.completionHandler = ^{ __strong typeof(weakSelf) strongSelf = weakSelf; if (strongSelf) { // 使用strongSelf进行操作 } };
- 方法:在
- 使用__block关键字(ARC环境下结合手动置nil)
- 方法:声明一个
__block
类型的变量指向self
,在completionHandler
中使用这个变量。并且在ViewController
即将释放时,手动将这个变量置为nil
。 - 原理:在ARC环境下,
__block
修饰的对象变量默认也是强引用。但是通过手动在合适的时机(如dealloc
方法中)将其置为nil
,可以打破循环引用。因为当这个变量置为nil
后,completionHandler
中对其的引用也会失效,从而打破了循环。
__block typeof(self) blockSelf = self; self.completionHandler = ^{ if (blockSelf) { // 使用blockSelf进行操作 } }; - (void)dealloc { blockSelf = nil; }
- 方法:声明一个