不同的闭包在 swift 中给出不同的保留循环结果

Different closures giving different results for retain cycles in swift

我正在阅读 Apple 的 Swift 编程语言指南。在关于闭包的强引用循环的部分,我尝试了一种不同类型的闭包,但它没有给出预期的输出。

class HTMLElement {

let name: String
let text: String?

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")
}

}

 var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
 println(paragraph!.asHTML)
 paragraph = nil

这段代码的输出是:

<p>hello, world</p>
p is being deinitialized
即使没有 [unowned self]

也会打印

"p is being deinitialised"

指南中的代码是:

class HTMLElement {

let name: String
let text: String?

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")
}

}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
 println(paragraph!.asHTML)
 paragraph = nil

仅当添加 [unowned self] 语句时才打印反初始化消息。

这两种闭包有什么区别?

在 Swift 书中的示例中,asHTML 是一个 闭包 :

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

self 持有对闭包的强引用(通过 asHTML 属性), 和 闭包持有对 self 的强引用(除非你添加 [unowned self]).

在您的示例中,asHTML 是 属性 类型 String:

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

右侧被评估一次,当惰性 属性 被访问时 第一次。创建(捕获 self)并评估闭包对象。之后,闭包对象被销毁(因为没有 存在对它的强烈引用)。特别是,捕获的参考 to self 已发布:不再关闭,没有保留周期。

好问题!为了探索它,我们应该将测试用例简化到演示它所需的最低限度:

// First case, a lazy string
class LazyVar {
    let name = "LazyVar"
    lazy var lazyName : String = { return self.name }()
    deinit {
        println("\(name) is being deinitialized")
    }
}

var lazyVar: LazyVar? = LazyVar()
println(lazyVar?.lazyName)
lazyVar = nil // Calls deinit

// Second case, a lazy ()->String
class LazyFunc {
    let name = "LazyFunc"
    lazy var lazyFunc: () -> String = {
        // [unowned self] in    // <-- This would break the loop
        return self.name
    }
    deinit {
        println("\(name) is being deinitialized")
    }
}

var lazyFunc: LazyFunc? = LazyFunc()
println(lazyFunc?.lazyFunc())
lazyFunc = nil // Doesn't call deinit

第一种情况,没有永久保留循环。当您访问 lazyName 时,会评估一个闭包 { return self.name }。该闭包捕获 self,计算字符串,然后 returns 字符串。该字符串分配给 属性。我们现在完成了闭包,所以它被释放了,它释放了 self。请注意,这里有一个简短的保留循环,但没关系。它最终会被打破,这才是最重要的。

在第二种情况下,有一个永久保留循环。当您访问 lazyFunc() 时,它会调用一个创建 新闭包 的闭包。那个新的闭包是 {return self.name},并且分配给了 属性。那个闭包保留 self,一个永不中断的保留循环也是如此。如标记的那样将 [unowned self] in 添加到 lazyFunc 将为您打破循环。