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

时间:2019-02-26 09:07:07

标签: swift function closures lazy-evaluation conceptual

我在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设置为e,就释放了nil。但是,在闭包anotherReturnX()中捕获了一个引用,因此f没有被释放。显然,这意味着闭包会捕获引用,而函数不会。

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

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

我已经阅读了一些相关文章,例如强/弱引用,保留周期,惰性存储属性。但是,许多人解释“会发生什么”,而对“为什么”却不多说。

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

更新:

由于Xcode编译器可以将return self.a重写为return a中的returnX(),因此闭包捕获引用而函数未捕获引用这一事实在我身上进一步得到了加强。但我无法在anotherReturnX中这样做。因此,我想我必须接受这一点,尽管函数和闭包是相似的,因为它们每个都是“一堆功能”,但函数 与不捕获引用的闭包不同。如果我要更深入地研究其背后的原因,那可能涉及Swift本身的设计吗?

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

1 个答案:

答案 0 :(得分:0)

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

这里的自我是强大的自我。 当一个对象强烈引用另一个对象时,ARC无法取消分配,因此会创建一个保留周期。引用应该是弱的,以避免在块内创建弱的self来避免保留周期。

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