Different closures giving different results for retain cycles in swift

試著忘記壹切 提交于 2019-12-02 01:52:22

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.

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.

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