【问题标题】:weak vs unowned in Swift. What are the internal differences?Swift 中的弱与无主。内部差异是什么?
【发布时间】:2017-08-08 02:45:21
【问题描述】:

我了解weakunowned 在Swift 中的用法和表面上的区别:

我见过的最简单的例子是,如果有DogBoneBone 可能对Dog 有弱引用(反之亦然),因为每个都可以独立存在彼此的。

另一方面,在HumanHeart 的情况下,Heart 可能有一个unowned 对人类的引用,因为一旦Human 变为..."取消引用”,Heart 不能再被合理地访问。这和CustomerCreditCard 的经典示例。

所以这不是重复的问题。


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

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

使用unowned 与通过! 隐式展开weak 变量相比,我可以看到的唯一实际优势是我们可以通过let 使unowned 引用保持不变。

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

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

我最想听听从事 Swift 编译器(或其他编译器)工作的人的意见。

【问题讨论】:

标签: swift compiler-optimization weak-references memory-safety


【解决方案1】:

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

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

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

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

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

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

因此,unowned 在适用的任何地方始终是首选。但除非绝对安全,否则切勿使用它!使用unowned,您放弃了自动内存管理和安全。你故意回到 ARC 之前的糟糕时光。

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

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

  • 它更高效,部分原因是相同的(展开总是会增加一层额外的间接性),部分原因是它减少了运行时暂存器列表要跟踪的弱引用。

【讨论】:

  • 谢谢,这是迄今为止最好的答案,实际上是在编译器级别的幕后发生的事情。你有关于weak 是计算密集型的想法的参考吗?
  • 您只需要想象一下 ARC 需要在幕后完成所有这些簿记工作。这个过程在精彩的 WWDC 2012 视频 406 中有很好的描述,其中介绍了 ARC。每个人都应该观看这个视频,因为 Swift ARC 就是 Objective-C ARC(事实上,我们现在知道 Chris Lattner 为 Objective-C 发明了 ARC 正是为了他可以创建 Swift)。
  • 无论如何,我的回答的重点是你的问题是基于一个错误的前提。 weakunowned 之间的区别可能看起来就像用一个词代替另一个词来代替 you,但它们是完全不同的东西——事实上,它们是不同的 种类 的事情。您可能想阅读我的在线书籍,其中我讨论了合成属性的内存管理策略(这与您所询问的基本相同)apeth.com/iOSBook/…weakweakassign 是 @987654341 @.
  • 对于没有问过这个问题的人来说,它们似乎是用一个词代替另一个词。所以你怀疑的问题的“前提”是问题......
  • @ephemer 好的,我明白你的意思了!我的问题是问题开始于“我明白”,而我在想,“不,你不明白”。但是现在我看到您在说:我知道 rule 说“仅当引用的对象始终存在并且对该对象的生命至关重要时才使用 unowned”。你想知道的是 why 这是规则(其次,如果你确实应该使用 unowned 而不是 weak,因为显然你总是 可以使用弱)。你是对的,这是一个非常好的问题。 (我希望我已经解释得很满意了。)
【解决方案2】:

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

您可以使用if letguard? 等来检查 weak 与 nil,但检查 unowned 是没有意义的,因为您认为这是不可能的。如果你错了,你就会崩溃。

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

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

您正在寻找的“它为什么存在”是 Swift 旨在能够编写系统代码(如 OS 内核),如果它们没有最基本的原语而没有额外的行为,它们可以不要那样做。

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

【讨论】:

  • 您似乎已经对此达成了普遍共识(weakunowned 更常见)但我不确定这是否能回答问题。似乎答案描述了表面的用法差异,而不是两者作为单独关键字存在的内部原因。不过,关于 Swift 也被设计用于系统编程的评论很好,谢谢。
  • 重新审视您的答案的另一个关键点是,您不能保证将无主引用设置为 nil(我猜它们通常不会并且将作为危险的悬空指针留下)。谢谢。
  • unowned(unsafe) 成为悬空指针。我会更新答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-04-11
  • 2019-07-18
  • 2013-12-22
  • 2014-05-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多