首页 > 解决方案 > 为什么闭包捕获引用而函数没有捕获引用?另外,为什么闭包声明需要“lazy”关键字?

问题描述

我在 Xcode Playground 中使用以下代码对其进行了试验:

class X {

    var a = 3

    init(a: Int) {
        self.a = a
    }

    deinit {
        print("\(self.a) is deallocated.")
    }

    func returnX() -> Int {
        return self.a
    }

    lazy var anotherReturnX: () -> Int = {
        return self.a
    }
}

var e: X? = X(a: 6)
print(e!.returnX())
e = nil // prints "6 is deallocated."

var f: X? = X(a: 7)
print(f!.anotherReturnX())
f = nil // prints nothing

从上面的代码中,我可以看到函数中没有捕获任何引用returnX(),因此e一旦我设置enil. 但是,在闭包中捕获了一个引用anotherReturnX(),因此f不会被释放。显然,这意味着闭包会捕获引用,而函数不会。

此外,当我第一次输入代码时,我没有lazy在闭包声明之前包含关键字,因为我认为没有必要这样做。但是它会触发编译时错误。我推断,由于闭包只能在实例化后访问,所以它必须访问实例化的self. 但是,既然我在这里声明的实际上是一个“匿名函数”,为什么self在实例化期间闭包会访问呢?

经过一番思考,我发现了更多的矛盾。例如,我知道在调用闭包时会捕获引用。但是,在初始化期间X,我只是闭包分配给变量而不调用它,与其他实例属性的声明相同。因此,闭包在初始化期间不应该做任何事情,并且编译没有关键字的代码lazy应该没问题。但是编译失败。我不确定我的理解出了什么问题。

我已经阅读了一些相关文章,例如强/弱引用、保留周期、惰性存储属性。然而,许多人解释“发生了什么”,并没有过多地谈论“为什么”。

所以除了标题中提出的问题之外,我还想澄清一下,是什么让函数和闭包彼此不同,从而导致上述情况发生?

更新:

闭包捕获引用而函数没有捕获引用这一事实在我身上进一步“强制”,因为根据 Xcode 编译器,我可以重写return self.areturn ain returnX(),但我不能在anotherReturnX. 因此,我想我不得不接受这一点,尽管函数和闭包是相似的,因为它们都是“一组功能”,但函数与闭包不同,它不捕获引用。如果我要深入了解这背后的原因,它可能会涉及到 Swift 本身的设计?

但是,我仍然不明白为什么lazy闭包声明需要关键字。

标签: swiftfunctionclosureslazy-evaluationconceptual

解决方案


lazy var anotherReturnX: () -> Int = {
    return self.a
}

这里的自我是一个坚强的自我。当一个对象强引用另一个对象时,ARC 不能解除分配,因此会创建一个保留循环。引用应该是弱的,以避免通过在块内创建弱自身来避免保留循环。

lazy var anotherReturnX: () -> Int = { [weak self] in
    return self?.a
}

推荐阅读