Swift 中闭包的强引用循环问题

3
我正在阅读文档(关于“自动引用计数”章节中的“闭包的强引用循环”),但我似乎无法确定在定义类时应该在闭包中保留对self(该类的实例)的强引用的情况。 捕获列表似乎总是避免内存泄漏的最佳解决方案,我真的想不出任何场景需要保持强引用循环。
以下是文档提供的示例:
class HTMLElement {

    let name: String
    let text: String?

    // Without Capture List 
    @lazy var asHTML: () -> String = {
        if let text = self.text {
            return "<\(self.name)>\(text)</\(self.name)>"
        } else {
            return "<\(self.name) />"
        }
    }

    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }

    deinit {
        println("\(name) is being deinitialized")
    }

}

class HTMLElement {

    let name: String
    let text: String?

    // With Capture List
    @lazy var asHTML: () -> String = {
        [unowned self] in
        if let text = self.text {
            return "<\(self.name)>\(text)</\(self.name)>"
        } else {
            return "<\(self.name) />"
        }
    }

    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }

    deinit {
        println("\(name) is being deinitialized")
    }

}

你的问题具体是什么? - holex
“我真的想不出任何场景,需要保持强引用循环” -> 还有吗? - Giovanni
那是一个不优化的代码示例,应该避免使用。 - Giovanni
1个回答

6
您需要在创建闭包以被对象或函数执行时保持对self的强引用,以防止self的生命周期与对象或函数不匹配。

例如:
class A {
    func do() {
        dispatch_async(dispatch_get_global_queue(0, 0)) {
            println("I printed \(self) some time in the future.")
        }
    }
}

var a : A? = A()
a.do()
a = nil // <<<

在箭头处,主函数体将释放对A的新实例的最后一个引用,但是派发队列需要保持对其的控制,直到闭包执行完成。


谢谢你。现在我明白了。但是你为什么认为强引用self是“默认”的行为呢?它似乎不是很内存友好的行为。谢谢! - Giovanni
将其作为强引用是默认的最安全行为,因为这可以保证当执行闭包时对象仍然存在 - 弱引用意味着它可能突然为空,而无主引用意味着它可能指向无效数据。此外,使用闭包作为参数传递给外部方法(例如某些未来外部事件的回调)比将其存储为创建闭包的类的属性要常见(有争议)。在这些情况下,以强引用捕获self通常是完全合理的做法。 - Wes Campaigne

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接