someFunction(completion: { [weak self] in
self?.variable = self!.otherVariable
})
Is this always safe? I access the optional self
in the beginning of the statement, and personally I assume that the second part of this statement will never be executed if self
is nil
. Is this true? If self
indeed is nil
, the second part will never happen? And it will never happen that self
could be 'nilled' during this single line of code?
The documentation clearly states that, if the left side of the assignment is determined to be nil, right side will not be evaluated. However, in the given example
self
is weak reference and may be released (and nullified) right after the optional check passes, but just before force-unwrap will happen, making the whole expression nil-unsafe.Optional Chaining from "The Swift Programming Language" gives the following example:
followed by (emphasis added):
Applied to your case: In
the right-hand side is not evaluated if
self
isnil
. Therefore the answer to your questionis "yes". With regard to the second question
My original assumption was that once
self
has been determined to be!= nil
, a strong reference toself!
is held throughout the evaluation of the statement, so that this can not happen. However (as @Hamish pointed out), this is not guaranteed. Apple engineer Joe Groff writes at Confirming order of operations in the Swift forum:No. You are not doing the "weak-strong dance". Do it! Whenever you use
weak self
, you should unwrap the Optional safely, and then refer only to the result of that unwrapping — like this:No, this is not safe
As pointed out by @Hamish in a comment below, Swift Compiler Engineer Joe Groff describes that there is no guarantee that a strong reference is held for the duration of the RHS' evaluation [emphasis mine]
Given the answer quoted answer by Joe Groff above, my previous answer is moot, but I will leave it here as a possibly interesting (albeit failed) journey into the depths of the Swift compiler.
Historical answer reaching an in-correct final argument, but through an interesting journey, nonetheless
I'll base this answer upon my comment to @appzYourLife:s deleted answer:
Let's have a look at your example, where
self
has been captured by aweak
reference and is notnil
when accessing the left hand side of the assignment expressionWe may look at the underlying treatment of
weak
(Swift) references in the Swift runtime,swift/include/swift/Runtime/HeapObject.h
specifically:The key here is the comment
Since this is based on backend runtime code comment, it is still somewhat speculative, but I would say that the above implies that when attempting to access the value pointed to by a
weak
reference, there reference will indeed be retained as a strong one for lifetime of the call ("... until returning").To try to redeem the "somewhat speculative" part from above, we may continue to dig into how Swift handles access of a value via a
weak
reference. From @idmean:s comment below (studying the generated SIL code for an example like the OP:s) we know that theswift_weakLoadStrong(...)
function is called.So we'll start by looking into the implementation of the
swift_weakLoadStrong(...)
function inswift/stdlib/public/runtime/HeapObject.cpp
and see where we'll get from there:We find the implementation of the
nativeLoadStrong()
method ofWeakReference
fromswift/include/swift/Runtime/HeapObject.h
From the same file, the implementation of
nativeLoadStrongFromBits(...)
:Continuing along the call chain,
tryRetain()
is a method ofHeapObjectSideTableEntry
(which is essential for the object lifecycle state machine), and we find its implementation inswift/stdlib/public/SwiftShims/RefCount.h
The implementation of the
tryIncrement()
method of theRefCounts
type (here invoked via an instance of atypedef
:ed specialization of it) can be found in the same file as above:I believe the comment here suffices for us to use this method as an end point: if the object is not deiniting (which we've assumed above that it does not, as the
lhs
of assignment in the OP:s example is assumed to be successful), the (strong) reference count on the object will be increased, and aHeapObject
pointer (backed by a strong reference count increment) will be passed to the assignment operator. We needn't study how the corresponding reference count decrement is eventually performed at the end of the assignment, but now know beyond speculation that the object associated with theweak
reference will be retained as a strong one for the lifetime of the assignment, given that it has not been freed/deallocated at the time of the left hand side access of it (in which case the right hand side of it will never be processed, as has been explained in @MartinR:s answer).BEFORE CORRECTION:
I think others have answered the details of your question far better that I could.
But aside from learning. If you actually want your code to reliably work then its best to do as such:
AFTER CORRECTION.
Thanks to MartinR's explanation below I learnt a great deal.
Reading from this great post on closure capturing. I slavishly thought whenever you see something in brackets
[]
it means it's captured and its value doesn't change. But the only thing we're doing in the brackets is that we areweak
-ifying it and making it known to ourselves that it's value could becomenil
. Had we done something like[x = self]
we would have successfully captured it but then still we'd have the problem of holding a strong pointer toself
itself and be creating a memory cycle. (It's interesting in the sense it's a very thin line from going to creating a memory cycle to creating a crash due to the value being deallocated because you weak-ified it).So to conclude:
[capturedSelf = self]
creates memory cycle. Not good!
(can lead to crash if you forcetheself
unwrap afterwards)guard let
is it utterly useless. Because the very next line, stillself
can becomenil
. Not good!(can lead to crash if you force
self
unwrap afterwards. Would go through ifself
is non-nil
. Would fail safely ifself
isnil
.) This is what most likely what you want. This is Good!Will fail safely if
self
was deallocated or proceed if notnil
. But it kinda defeats the purpose, because you shouldn't need to communicate withself
when it removed it's own reference. I can't think of a good use case for this. This is likely useless!