Different closures giving different results for retain cycles in swift

。_饼干妹妹 提交于 2019-12-02 05:01:39

问题


I am reading Apple's Swift Programming Language Guide. In the part about Strong Reference Cycle for closures, I tried a different type of closure but it did not give the expected output.

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

The output of this code is :

<p>hello, world</p>
p is being deinitialized

"p is being deinitialised" is printed even without [unowned self]

The code in the guide is:

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

This prints the deinitialiser message only when the [unowned self] statement is added.

What is the difference between both the closures?


回答1:


Great question! To explore it, we should simplify the test case to the minimum required to demonstrate it:

// 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

In the first case, there is no permanent retain loop. When you access lazyName, a closure is evaluated { return self.name }. That closure captures self, computes a string, and returns the string. The string is assigned to the property. We're now done with the closure, so it's released, and it releases self. Note that there is a brief retain loop here, but that's ok. It is eventually broken, and that's all that matters.

In the second case, there is a permanent retain loop. When you access lazyFunc(), it calls a closure that creates a new closure. That new closure is {return self.name}, and that is assigned to the property. And that closure retains self, so is a retain loop that is never broken. Adding [unowned self] in to lazyFunc as marked would break the loop for you.




回答2:


In the example from the Swift book, asHTML is a closure:

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

self holds a strong reference to the closure (via the asHTML property), and the closure holds a strong reference to self (unless you add [unowned self]).

In your example, asHTML is a property of type String:

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

The right-hand side is evaluated once, when the lazy property is accessed the first time. A closure object is created (capturing self) and evaluated. After that, the closure object is destroyed (because no strong references exist to it). In particular, the captured reference to self is released: No closure anymore, no retain cycle.



来源:https://stackoverflow.com/questions/30576146/different-closures-giving-different-results-for-retain-cycles-in-swift

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!