【问题标题】:Swift array messing with the release of weak variablesSwift 数组弄乱了弱变量的释放
【发布时间】:2016-11-28 01:24:49
【问题描述】:

Swift 数组是值类型,而类是引用类型,所以我认为像 [SomeClass] 这样的东西会创建一个包含对 SomeClass 实例的引用的数组。

但是,在 Swift REPL 中,会发生以下情况:

  1> class SomeClass {}
  2> var obj: SomeClass? = SomeClass()
obj: SomeClass? = 0x0000000101100050
  3> weak var weakObj = obj
weakObj: SomeClass? = 0x0000000101100050
  4> var array = [SomeClass?]()
array: [SomeClass?] = 0 values
  5> array.append(obj)
  6> print(obj, weakObj)
Optional(SomeClass) Optional(SomeClass)
  7> array.removeFirst()
$R0: SomeClass? = 0x0000000101100050
  8> obj = nil
  9> print(obj, weakObj)
nil Optional(SomeClass)
 10> print(array)
[]
 11> print(Unmanaged.passUnretained(weakObj!).toOpaque())
0x0000000101100050

我认为在将obj 附加到array 之后,0x0000000101100050 的实例的引用计数应该是 2,并且一旦调用了 obj = nilarray.removeFirst(),两个引用都被删除了,因此该实例应该是发布。

但是,情况似乎并非如此。如果没有数组部分,obj 会按原样释放。我在这里错过了什么?

添加 似乎removeFirst()popLast() 和类似功能正在发生一些事情。 (可能是一个错误?)

将数组索引处的对象直接设置为nil 就可以了。

 102> obj = SomeClass()
 103> (weakObj, array) = (obj, [obj])
 104> print(obj, weakObj, array)
Optional(SomeClass) Optional(SomeClass) Optional([Optional(SomeClass)])
 105> obj = nil
 106> array?[0] = nil
$R14: ()? = nil
 107> print(obj, weakObj, array)
nil nil Optional([nil])

但是,当使用removeLast()popLast()时,weakObj只有在array本身被释放时才会被释放。

【问题讨论】:

  • 我认为$R0 可能有很强的参考价值
  • 当您从数组中删除时,removeFirst 函数将返回已删除的对象。这被$R0 强烈引用,让您的对象保持活力。看我的回答。

标签: arrays swift automatic-ref-counting reference-counting


【解决方案1】:

一般来说,REPL(或 Playground)不是试验 Swift ARC 工作原理的好地方。

尝试将您的代码作为 macOS 的命令行项目:

import Foundation

class SomeClass {}
var obj: SomeClass? = SomeClass()
weak var weakObj = obj
var array = [SomeClass?]()
array.append(obj)
print(obj, weakObj) //->Optional(SwiftArrayARC.SomeClass) Optional(SwiftArrayARC.SomeClass)
array.removeFirst()
obj = nil
print(obj, weakObj) //->nil nil
print(array) //->[]
print(Unmanaged.passUnretained(weakObj!).toOpaque()) //=>fatal error: unexpectedly found nil while unwrapping an Optional value

这不是你所期望的吗?

REPL 可能会为每一行的结果保留强引用,以便您以后使用它们,因此,在 REPL 环境中,您无法获得与实际应用中显示的完全相同的行为。

创建一个简单的命令行项目并在其中进行试验。 (注意:LLDB 也可能保持强引用,不要依赖它。)

【讨论】:

  • 很高兴知道!我知道 Playground 周围有物体,所以我正在试验 REPL。不知道 REPL 也这样做
【解决方案2】:

您的问题是7 线上的array.removeFirst() 正在返回已删除的对象。 repl 将它分配给 $R0 为您,这是使您的对象保持活动状态的强引用。

如果您使用_ = 显式丢弃结果,您的代码将按预期运行:

Welcome to Apple Swift version 3.0.1 (swiftlang-800.0.58.6 clang-800.0.42.1). Type :help for assistance.
  1> class SomeClass {}
  2> var obj: SomeClass? = SomeClass()
obj: SomeClass? = 0x00000001006005b0
  3> weak var weakObj = obj
weakObj: SomeClass? = 0x00000001006005b0
  4> var array = [SomeClass?]()
array: [SomeClass?] = 0 values
  5> array.append(obj)
  6> print(obj, weakObj)
Optional(SomeClass) Optional(SomeClass)
  7> _ = array.removeFirst()
  8> obj = nil
  9> print(obj, weakObj)
nil nil
 10> print(array)
[]
 11> print(Unmanaged.passUnretained(weakObj!).toOpaque())
fatal error: unexpectedly found nil while unwrapping an Optional value
2016-11-27 20:58:42.066831 repl_swift[62143:10516084] fatal error: unexpectedly found nil while unwrapping an Optional value
Current stack trace:
0    libswiftCore.dylib                 0x00000001002bccc0 swift_reportError + 132
1    libswiftCore.dylib                 0x00000001002da070 _swift_stdlib_reportFatalError + 61
2    libswiftCore.dylib                 0x00000001000d00a0 specialized specialized StaticString.withUTF8Buffer<A> ((UnsafeBufferPointer<UInt8>) -> A) -> A + 355
3    libswiftCore.dylib                 0x000000010024c210 partial apply for (_fatalErrorMessage(StaticString, StaticString, StaticString, UInt, flags : UInt32) -> Never).(closure #2) + 109
4    libswiftCore.dylib                 0x00000001000d00a0 specialized specialized StaticString.withUTF8Buffer<A> ((UnsafeBufferPointer<UInt8>) -> A) -> A + 355
5    libswiftCore.dylib                 0x00000001002043d0 specialized _fatalErrorMessage(StaticString, StaticString, StaticString, UInt, flags : UInt32) -> Never + 96
7    repl_swift                         0x0000000100001420 main + 0
8    libdyld.dylib                      0x00007fffaaec7254 start + 1
Execution interrupted. Enter code to recover and continue.
Enter LLDB commands to investigate (type :help for assistance.)
 12>

【讨论】:

  • 我想我应该阅读一下 REPL 的工作原理。不知道 $R0 是你可以在 REPL 中引用的东西。
  • 是的,它真的很方便。如果您不将表达式的结果分配给变量(或使用 _ 丢弃它),那么 REPL 会为您创建一个新的 $R 变量。使用您认为不想使用的过去的值会非常方便
  • @BridgeTheGap 我回答你的问题了吗?
  • 糟糕。忘记检查了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-28
相关资源
最近更新 更多