BridgeTheGap BridgeTheGap - 7 days ago 5
Swift Question

Swift array messing with the release of weak variables

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.

Answer

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>
Comments