I experimented it with the following code in Xcode Playground:
class X {
var a = 3
init(a: Int) {
self.a = a
}
deinit {
print("\(self.a) is deallocated.")
}
func returnX() -> Int {
return self.a
}
lazy var anotherReturnX: () -> Int = {
return self.a
}
}
var e: X? = X(a: 6)
print(e!.returnX())
e = nil // prints "6 is deallocated."
var f: X? = X(a: 7)
print(f!.anotherReturnX())
f = nil // prints nothing
From the above code, I can see that no reference is captured in the function returnX()
, thus e
is deallocated once I set e
to nil
. However, a reference is captured in the closure anotherReturnX()
, thus f
is not deallocated. Apparently, this implies that a closure captures references while a function does not.
Additionally, when I first type out the code, I didn't include lazy
keyword before the closure declaration, as I thought it would be unnecessary to do so. However it triggers a compile-time error. I infer that since the closure can only be accessed after instantiation, it must access to the instantiated self
. But since what I am declaring here is effectively an "anonymous function", why would the closure access to self
during instantiation anyway?
After putting in some thoughts, I found more contradictions. For instance, I understand that a reference is captured when a closure is called. However, during initialisation of X
, I am simply assigning a closure to a variable without calling it, same as the declaration of other instance properties. Thus the closure should not do anything during initialisation, and compiling the code without keyword lazy
should be fine. But compilation fails. I am not sure what goes wrong in my understanding.
I have read some related articles, such as strong/weak reference, retain cycle, lazy stored property. However, many explain "what happens" and do not say much about "why".
So other than the question raised in the title, I would also like to clarify, what makes function and closure different from each other such that the above situation happens?
Update:
The fact that closure captures reference while function does not is further "enforced" on me, since, according to the Xcode Compiler, I can rewrite return self.a
as return a
in returnX()
, but I cannot do so in anotherReturnX
. As such, I guess I would have to accept that, although function and closure are similar because each of them is "a bundle of functionalities", function is different from closure that it does not capture references. If I were to go deeper in the reason behind this, it would probably involve the design of Swift itself?
However, I still cannot understand why lazy
keyword is required for closure declaration.