This is an almost “trivial” thing (and I think we all know the answer), but even after hours of digging, I’m surprised I found absolutely NO clearly stated guarantee in the Swift language official specification about the lifetime of self
(as well as other pass-by-reference parameters) on an instance method.
In other words, for the following code:
class MyClass {
func instanceMethod() {
print("Method start")
Thread.sleep(forTimeInterval: 2.0) // Imagine during this sleep, all strong refs of self disappear.
// Question: Is 'self' guaranteed to remain valid
// throughout this entire method execution?
self.doSomeWork() // is self still here?
print("Method end")
}
func doSomeWork() {
// Additional work using self
}
}
What happens in the following cases?
If instanceMethod
indeed starts executing, how do we know doSomeWork
will run? (I know the answer is yes, but who says so?)
// Case 1: weak reference
let closure = { [weak myClassInstance] in
myClassInstance?.instanceMethod() // does doSomeWork execute?
}
// Case 2: unowned reference
let closure = { [unowned myClassInstance] in
myClassInstance.instanceMethod() // does doSomeWork execute?
}
What I have pieced together:
- This forum post suggests a strong reference is loaded on weak reference accesses – the SIL emits
strong_retain
(or similar). Joe Groff states they are released at or after “the last formal use of the strong reference” (what precisely does this mean? - This reply also seems to support the idea of a lifetime extension for the object for a method
- The Swift Runtime correspondingly contains methods such as
swift_weakLoadStrong
andswift_unownedLoadStrong
method. This suggests thatunowned
andweak
references generatestrong
references for some duration - We know that since Swift 4.2, the calling convention for methods has shifted to Guaranteed +0 from +1. This means the callee method no longer has a retain for its parameters within its body.
- Swift SIL – “A guaranteed direct parameter is like an unowned direct parameter value, except that it is guaranteed by the caller” – but this says nothing about the behavior of ARC in the caller, especially relating to weak or unowned references.
Can we add a guarantee about this in the ARC chapter of Swift language specs? Or is the (almost bedrock for me) assumption about ARC and Swift not actually something that we can take for granted?