20

我了解Swift之间的用法weak和表面上的区别:unowned

我见过的最简单的例子是,如果存在 aDog和 a Bone,则 theBone可能对 the 有弱引用Dog(反之亦然),因为它们可以相互独立地存在。

另一方面,在 aHuman和 a的情况下Heart, theHeart可能有unowned对人类的引用,因为一旦Human变得......“取消引用”,Heart就不能再合理地访问。那和带有 和 的经典Customer例子CreditCard

所以这不是重复的问题。


我的问题是,拥有两个如此相似的概念有什么意义?有什么内部差异需要使用两个关键字来表示本质上 99% 相同的东西?问题是为什么存在差异,而不是差异是什么。

鉴于我们可以像这样设置一个变量:weak var customer: Customer!unowned变量是非可选的优点是有争议的。

我可以看到使用vs 隐式解包变量 via的唯一实际优势是我们可以通过.unownedweak!unownedlet

...也许编译器可以因此进行更有效的优化。

这是真的吗,还是幕后发生的其他事情为保留这两个关键字提供了一个令人信服的论据(尽管细微的区别——基于 Stack Overflow 的流量——显然让新手和经验丰富的开发人员都感到困惑)。

我很想听听那些在 Swift 编译器(或其他编译器)上工作过的人的意见。

4

2 回答 2

20

我的问题是,拥有两个如此相似的概念有什么意义?有什么内部差异需要使用两个关键字来表示本质上 99% 相同的东西?

它们一点也不相似。它们尽可能地不同。

  • weak是一个高度复杂的概念,在引入 ARC 时引入。它执行了近乎神奇的任务,允许您防止保留循环(通过避免强引用),而不会在引用的对象不存在时冒着因悬挂指针而崩溃的风险——这在 ARC 之前一直发生被介绍了。

  • unowned,另一方面,是非-ARC 弱(具体来说,它与非 ARC 相同assign)。在引入 ARC 之前,这是我们过去不得不冒险的原因,也是导致如此多崩溃原因这是非常危险的,因为如果引用的对象不存在,您可能会得到一个悬空指针和崩溃。

差异的原因在于weak,为了实现它的奇迹,运行时需要大量额外的开销,由编译器在幕后插入。weak引用是为您管理内存的。特别是,运行时必须维护以这种方式标记的所有引用的暂存器,跟踪它们,以便如果弱引用的对象不存在,运行时可以找到该引用并将其替换nil为防止悬空指针。

因此,在 Swift 中,weak引用始终是对 Optional 的引用(正好可以用 替换nil)。这是额外的开销来源,因为使用 Optional 需要额外的工作,因为它必须始终打开才能完成任何事情。

出于这个原因,unowned在任何适用的地方总是首选。但除非绝对安全,否则切勿使用它!有了它unowned,您就抛弃了自动内存管理和安全性。你故意回到 ARC 之前的糟糕时光。

在我的使用中,常见的情况出现在闭包需要一个捕获列表self来避免保留循环的情况下。在这种情况下,几乎总是可以[unowned self]在捕获列表中说。当我们这样做时:

  • 这对程序员来说更方便,因为没有什么可以解开的。[weak self]将是一个 Optional 需要展开才能使用它。

  • 它更有效,部分原因是出于相同的原因(展开总是添加额外的间接级别),部分原因是它减少了运行时暂存器列表要跟踪的弱引用。

于 2017-03-17T01:45:28.057 回答
9

weak引用实际上设置为 nil,当引用对象解除分配并且一个设置为 nil 时,您必须检查它,unowned但您不必检查它。

你可以用, ,等来检查 aweak和 nil ,但是检查 an 是没有意义的,因为你认为那是不可能的。如果你错了,你就会崩溃。 if letguard?unowned

我发现在实践中,我从不使用 unowned。性能损失很小,但使用弱的额外安全性对我来说是值得的。

我会将无主使用留给需要优化的非常具体的代码,而不是一般的应用程序代码。

您正在寻找的“它为什么存在”是 Swift 旨在能够编写系统代码(如 OS 内核),如果它们没有最基本的原语而没有额外的行为,他们就无法做到这一点.

注意:我之前在这个答案中说过 unowned 没有设置为 nil。这是错误的,a bareunowned被设置为 nil。Aunowned(unsafe)未设置为 nil,可能是一个悬空指针。这是为了满足高性能需求,通常不应出现在应用程序代码中。

于 2017-03-16T18:44:23.597 回答