Reputation: 3601
Swift arrays is that they are value types while classes are reference types, so I thought having something like [SomeClass]
would create an array containing the references to SomeClass
instances.
However, in Swift REPL, the following happens:
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
I thought the reference count of the instance at 0x0000000101100050
should be 2 after appending obj
to array
, and once obj = nil
and array.removeFirst()
were called, both references were removed, therefore the instance should be released.
However, this doesn't seem to be the case. Without the array part, obj
is released like it should. What am I missing here?
ADDED
It seems like there is something going on with removeFirst()
, popLast()
and similar functions. (Possibly a bug?)
Setting the object at the array index directly to nil
works just fine.
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])
However, when using removeLast()
or popLast()
, weakObj
will be released only when array
itself is released.
Upvotes: 1
Views: 396
Reputation: 63271
Your issue is that array.removeFirst()
on line 7
is returning the object that was removed. The repl assigns this to $R0
for you, which is the strong reference keeping your object alive.
Your code behaves as you expect if you explicitly discard the result with _ =
:
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>
Upvotes: 1
Reputation: 47886
Generally speaking, REPL (or Playground) is not a good place to experiment how Swift ARC are working.
Tried your code as a Command Line project of 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
Isn't this what you expect?
REPL may keep strong references for the results of each line to enable you to use them later, so, in REPL environment, you cannot get exactly the same behaviour as shown in the actual app.
Create a plain Command Line project and experiment in it. (Caution: LLDB also may keep strong reference. Do not rely on it.)
Upvotes: 2