Swift 数组干扰弱变量的释放
Swift array messing with the release of weak variables
Swift 数组是值类型,而 类 是引用类型,所以我认为 [SomeClass]
之类的东西会创建一个包含对 [=14= 的引用的数组] 实例。
但是,在 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
我认为 0x0000000101100050
处的实例的引用计数在将 obj
附加到 array
之后应该是 2,一旦 obj = nil
和 array.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
本身被释放时才会被释放。
一般来说,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 也可能保留强引用。不要依赖它。)
您的问题是第 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>
Swift 数组是值类型,而 类 是引用类型,所以我认为 [SomeClass]
之类的东西会创建一个包含对 [=14= 的引用的数组] 实例。
但是,在 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
我认为 0x0000000101100050
处的实例的引用计数在将 obj
附加到 array
之后应该是 2,一旦 obj = nil
和 array.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
本身被释放时才会被释放。
一般来说,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 也可能保留强引用。不要依赖它。)
您的问题是第 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>