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

问题描述 投票:0回答:2

我在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关键字。

swift function closures lazy-evaluation conceptual
2个回答
0
投票
lazy var anotherReturnX: () -> Int = {
    return self.a
}

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

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

0
投票

returnX是班级的一种方法。方法不捕获变量。 self是方法中的隐式局部变量,在调用方法时会隐式传递给方法。

anotherReturnX是一个懒惰分配关闭的财产。该闭包捕获了其中使用的外部变量,包括self。捕获创建了从闭包到X实例的强引用,结合X实例到闭包的强引用,创建了一个保留周期。

© www.soinside.com 2019 - 2024. All rights reserved.