This is not another question about [weak self]
. This is about use of variables not contained by self
, but rather by the wrapping function.
func someFunction(){
someOtherFunction(completionBlock:{ [weak self] in
self?.doStuff()
})
}
As far as I understand, I need the [weak self]
in order to prevent a retain cycle.
But what if I need to use a variable from the wrapping function, like this:
func someFunction(){
let someVariable = MyObject()
someOtherFunction(completionBlock:{ [weak self] in
self?.doStuff(with: someVariable)
})
}
This works, which makes me wonder.. How, and how long is someVariable
held in memory? Can it create its own tiny retain cycle where my completion block strongly references the local someVariable
? How will they be released? Should I add [weak self, weak someVariable]
in the block? But then, won't someVariable
be released immediately after I call someOtherFunction
, because it's the end of this function - and the end of someVariable
's lifetime..?
I'm having trouble completely understanding references, and can't see how my completionBlock
and someVariable
will ever be released.. Are blocks even released?
Any variable referenced inside a closure will be strongly retained by that closure. You can adjust that by including a closure capture list (e.g. [weak self]
), which allows you to specify the particular memory management pattern of references captured in the closure.
func someFunction(){
let someVariable = MyObject()
someOtherFunction(completionBlock:{ [weak self] in
self?.doStuff(with: someVariable)
})
}
Here, someVariable
is retained by the closure, as you have stated. In this case, it has to be, because nobody else cares about it. As you've mentioned in comments, if you used a capture list of [weak someVariable]
then it would always be nil when the completion block executed, as it has gone out of scope in it's original function.
A "tiny retain cycle" isn't being created. A retain cycle has to be a cycle - that is, A holds a strong reference to B, which holds a strong reference to A. someVariable
doesn't have references to anything.
Once someOtherFunction
has finished with the reference to the completion closure, everything goes away. The closure is just another variable as far as someOtherFunction
is concerned, and it will survive as long as it is in scope.
Should I weakify “local” variables used in a block? - no, as they will then be nil by the time the block comes to use them.
I would like to mention not so clear option, what is very usual place to create retain cycle and where you should be aware of weaken variables.
Lets consider this situation:
func someFunction() {
let object = Something()
object.handler = { [weak self] in
self?.doStuff(with: object)
}
}
Now there is retain cycle, and object
cannot be deallocated until somebody manually unset the handler
. Because now object strongify itself in the captured block.
So better solution is:
func someFunction() {
let object = Something()
object.handler = { [weak self, unowned object] in
self?.doStuff(with: object)
}
}
And good practice is pass the object
as argument in handler
func someFunction() {
let object = Something()
object.handler = { [weak self] (object) in
self?.doStuff(with: object)
}
}
So signature of this should look like:
class Something {
var handler:((Something) -> Void)?
deinit {
print("retain cycle is not here")
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With