面试题答案
一键面试闭包引起内存泄漏的原因
在Swift中,当闭包捕获了一个对象,并且这个闭包的生命周期比捕获的对象长时,就可能会导致内存泄漏。因为闭包持有对该对象的强引用,使得对象无法被释放,即使对象本身不再被其他地方使用。
代码示例
class SomeClass {
let name: String
init(name: String) {
self.name = name
print("\(name)初始化")
}
deinit {
print("\(name)销毁")
}
}
class AnotherClass {
var closure: (() -> Void)?
var someObject: SomeClass?
init() {
someObject = SomeClass(name: "SomeObject")
closure = { [unowned self] in
print(self.someObject?.name ?? "对象已释放")
}
}
deinit {
print("AnotherClass销毁")
}
}
var test: AnotherClass? = AnotherClass()
test = nil
在上述代码中,如果闭包 closure
不使用 [unowned self]
或者 [weak self]
来捕获 self
,闭包会强引用 self
,而 self
又持有 someObject
,这样即使 test
被设为 nil
,SomeClass
实例和 AnotherClass
实例也无法被释放,导致内存泄漏。
修复策略和方法
- 使用
weak
引用:在闭包捕获列表中使用weak
关键字来捕获对象,weak
引用不会对对象产生强引用,当对象被释放时,weak
引用会自动变为nil
。
closure = { [weak self] in
guard let strongSelf = self else { return }
print(strongSelf.someObject?.name ?? "对象已释放")
}
- 使用
unowned
引用:unowned
引用也不会对对象产生强引用,但与weak
不同的是,unowned
引用在对象被释放后不会变为nil
,所以使用unowned
时要确保被引用的对象在闭包执行期间不会被释放,否则会导致运行时错误。
closure = { [unowned self] in
print(self.someObject?.name ?? "对象已释放")
}