MST

星途 面试题库

面试题:Objective-C中Block引起的循环引用问题及解决

假设有一个视图控制器ViewController,其中有一个属性是Block类型的completionHandler,在Block内部又使用了self。请详细说明这种情况下可能会出现的循环引用问题,以及至少两种解决这种循环引用的方法,并解释每种方法的原理。
21.4万 热度难度
编程语言Objective-C

知识考点

AI 面试

面试题答案

一键面试

循环引用问题

在这种情况下,当completionHandler被持有(比如传递给其他对象并在之后调用),由于completionHandler内部使用了selfself就会持有completionHandler,而completionHandler又持有self,从而形成循环引用。这会导致ViewController及其相关资源在其生命周期结束时无法被释放,造成内存泄漏。

解决方法及原理

  1. 使用__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进行操作
        }
    };
    
  2. 使用__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;
    }