面试题答案
一键面试ARC检测和处理循环引用场景的机制
ARC 通过跟踪对象的引用计数来管理内存。当一个对象的引用计数变为 0 时,ARC 会自动释放该对象所占用的内存。在循环引用场景下,两个或多个对象相互持有强引用,导致它们的引用计数永远不会变为 0 ,从而造成内存泄漏。ARC 采用弱引用(weak)和无主引用(unowned)来打破这种循环引用。
常见循环引用场景及解决办法
- 闭包引起的循环引用
- 场景:当一个闭包捕获了它所在类的实例(
self
),同时这个闭包又被该实例所持有,就会形成循环引用。例如,一个视图控制器持有一个闭包属性,闭包内又访问了视图控制器的属性或方法。 - 解决办法:使用
[weak self]
或[unowned self]
在闭包捕获列表中捕获self
。
- 场景:当一个闭包捕获了它所在类的实例(
class ViewController: UIViewController {
var closure: (() -> Void)?
override func viewDidLoad() {
super.viewDidLoad()
closure = { [weak self] in
guard let self = self else { return }
print(self.view)
}
}
}
- 两个类相互持有强引用
- 场景:假设有两个类
ClassA
和ClassB
,ClassA
有一个ClassB
类型的强引用属性,ClassB
也有一个ClassA
类型的强引用属性。
- 场景:假设有两个类
class ClassA {
var b: ClassB?
deinit {
print("ClassA deinit")
}
}
class ClassB {
var a: ClassA?
deinit {
print("ClassB deinit")
}
}
如果这样创建实例:
var a = ClassA()
var b = ClassB()
a.b = b
b.a = a
a = nil
b = nil
// 此时ClassA和ClassB不会被销毁,因为它们相互持有强引用
- **解决办法**:将其中一个类的引用改为弱引用或无主引用。例如将`ClassB`中的`a`属性改为弱引用:
class ClassB {
weak var a: ClassA?
deinit {
print("ClassB deinit")
}
}
这样当a
和b
被置为nil
时,两个实例就会被正常销毁。