MST
星途 面试题库

面试题:Swift闭包引起的内存泄漏及修复

在Swift中,闭包常常是导致内存泄漏的一个重要因素。请描述闭包是如何引起内存泄漏的,给出一个具体的代码示例,并阐述修复这种由闭包导致的内存泄漏的策略和方法。
29.7万 热度难度
编程语言Swift

知识考点

AI 面试

面试题答案

一键面试

闭包引起内存泄漏的原因

在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 被设为 nilSomeClass 实例和 AnotherClass 实例也无法被释放,导致内存泄漏。

修复策略和方法

  1. 使用 weak 引用:在闭包捕获列表中使用 weak 关键字来捕获对象,weak 引用不会对对象产生强引用,当对象被释放时,weak 引用会自动变为 nil
closure = { [weak self] in
    guard let strongSelf = self else { return }
    print(strongSelf.someObject?.name ?? "对象已释放")
}
  1. 使用 unowned 引用unowned 引用也不会对对象产生强引用,但与 weak 不同的是,unowned 引用在对象被释放后不会变为 nil,所以使用 unowned 时要确保被引用的对象在闭包执行期间不会被释放,否则会导致运行时错误。
closure = { [unowned self] in
    print(self.someObject?.name ?? "对象已释放")
}