Should I weakify "local" variables used in a block?

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")
    }
}

Tags:

Ios

Swift