I want to understand in depth about closures and their mechanism of being allocated and usage. After reading and using them a while I have come up with some questions that are really eating up my head:
Question - 1
How is a closure used as a variable different from a normal variable from memory allocation point of view?
eg: let x:NSString ={}()
& let x = NSString()
Question-2
How exactly the memory allocation happens for a block?
Please kindly explain these so as that any readers having similar kind of doubts might be beneficial from it.
**EDIT*
The answer to this question by Shadow was to an other direction of this question which has been edited.
For stored properties with default values or default closures memory allocates immideatly, before init
method called. You can not use self
or properties for defining other property by default value.
Closures from your example usually used on this step to define lazy
properties; they should be var
and marked with lazy
keyword:
class myClass {
let x: NSString? = nil
let q: NSString? = {return "q"}() //allocates before class init; deallocates imidiatly
lazy var y: NSString = {
let z = self.x
return z ?? ""
}() //allocates on first property call; deallocates imidiatly
}
Memory for lazy properties will be allocated on first property call. Construction {/*...*/}()
means that this closure will be executed just at call moment and will return result of calculation (you can look at this like at unnamed function), not reference to this closure. This is very important moment: you are not retain closure, this closure allocates only for execution moment and deallocates just after return, so you don't need care about strong cycle reference problem.
Other thing - when you saving reference to closure:
class myClass {
let x: NSString? = nil
var closure: () -> NSString = {return "q"} //allocates before class init; deallocates on references release
lazy var lazyClosure: () -> NSString = {
let z = self.x
return z ?? ""
} //allocates on first property call; deallocates on references release
}
Memory for this closures allocates at same time as in previous case, but they will continue to live until have any references. You can look at closures here like at separate objects. Cycle reference problem can arise here. closure
is not capture anything, so there are no problems with it, but lazyClosure
capture self
. If we will never call lazyClosure
there will be no problems, since this closure will never allocates. But if we will call it, it will be allocated, it will capture self
, and there will be strong cycle reference: self
points to lazyClosure
instance, lazyClosure
points to self
instance. To solve this problem you should make one of references weak, you should use capture list: insert [weak self] in
or [unowned self] in
in lazyClosure
body. [unowned self] in
in our case, since if property called, then self
is not nil
.
lazy var lazyClosure: () -> NSString = {
[unowned self] in
let z = self.x
return z ?? ""
}