我想用Swift中的闭包周期做一些实验。所以我从一个操场和example in the Swift documentation开始。

class HTMLElement {
    let name: String
    let text: String?

    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 {
        print("\(name) is being deinitialized")
    }
}

var element = HTMLElement(name: "Travis", text: "Griggs")
print(element.asHTML()) // force the cycle
element = HTMLElement(name: "Bat", text: "Man") // encourage first element to deallocate

正如所料,由于周期的原因,没有任何deinit的提示。如果我将asHTML初始化为一个不捕获自身的闭包,我就可以打破这个循环。例如。
var element = HTMLElement(name: "Travis", text: "Griggs")
element.asHTML = { "No Cycles Here" }
print(element.asHTML()) // do the print
element = HTMLElement(name: "Bat", text: "Man") // encourage first element to deallocate

或者我可以将默认初始化更改为包括:
{ [ weak self] in
    guard let self = self else { return "-from-the-dead-" }
    ...
}

也做正确的事。但我认为可行的方法,似乎并不奏效。我的很多闭包看起来就像
{ self.doSomething() }

其中asHTML基本上与闭包签名具有相同的签名。在这种情况下,闭包似乎只是一个额外的包装器。想象一下,如果我向具有doSomething签名的HTMLElement添加一个方法:
extension HTMLElement {
    func defaultHTML() -> String {
        return "\(self.name) = \(self.text ?? "(there is no text)")"
    }
}

然后直接将() -> String属性设置为:
var element = HTMLElement(name: "Travis", text: "Griggs")
element.asHTML = element.defaultHTML
print(element.asHTML())
element = HTMLElement(name: "Bat", text: "Man") // encourage first element to deallocate

出于某种原因,我认为这不会造成一个循环。但我似乎错了?asHTMLelement。这有点违反直觉,因为我没有用大括号来结束。但看起来引用一个活动实例的方法确实做到了这一点?这是怎么回事?假设没有其他解释,在这种情况下有没有办法打破这个循环?或者我总是需要明确我的闭包,比如:
element.asHTML = { [weak element] in element?.defaultHTML() ?? "-yo-text-be-gone-" }

最佳答案

defaultHTML()不仅仅是一个函数,它还是一个实例方法。它在一个实例上运行,在您的例子中,self。您的闭包不能只记住“just calldefaultHTML”,而不记住要调用哪个实例。
因此,当您创建一个类似于{ self.defaultHTML }的闭包时,您将创建一个堆分配的ARC-managed框,其中包含对self的强引用和指向defaultHTML实例方法实现的指针。

08-16 01:40