Home > OS >  Swift, escaping closures and retaining self. Is it beneficial to unwrap self?
Swift, escaping closures and retaining self. Is it beneficial to unwrap self?

Time:12-06

I've been wondering if unwrapping weak self within the escaping closure's scope brings some benefits other than aesthetical ones? Consider those two examples:

When we unwrap self:

    func test() {
        Test.closureFunction { [weak self] parameter in
            guard let self = self else { return }
            self.someFunction(parameter)
        }
    }

When we don't unwrap self:

    func test() {
        Test.closureFunction { [weak self] parameter in
            self?.someFunction(parameter)
        }
    }

Could there be a scenario when not unwrapped self (1st example) may become nil as a result of some other asynchronous operation, thus the execution of the scope may differ from when we unwrap self (2nd example)? I believe it's a possible scenario, but I may be wrong.

I think that we may still want to execute an operation from within the escaping closure's scope while self is already nil. After this escaping closure finishes its scope the unwrapped self is released.

Thank you

CodePudding user response:

The answer is "it depends."

If you use a guard let, your closure captures self at the beginning of the closure. Self will not get released until your closure has finished running. Also, you won't need to unwrap it each time you use it (The "aesthetic" part)

In most cases, this makes sense, since once you start doing work in your closure, you likely want to do all that work.

If you don't use a guard let, your instance of self could get released in the middle of your closure's execution.

If the owner of your object (whoever is keeping a strong reference to self) might release the object while your closure is running and that means there is no point in continuing (which will depend on your use-case) then don't use a guard let and instead keep unwrapping, and/or keep checking to see if self is nil, and returning if it is nil.

CodePudding user response:

Duncan C explains the issues well, but I think a simple example will make it clear:

// In this version, `someFunction` and `otherFunction` will either both execute
// or both will not execute. That is typically what you'd want.
Test.closureFunction { [weak self] parameter in
    guard let self = self else { return }
    self.someFunction(parameter)
    self.otherFunction(parameter)
}

// In this version, it is possible for `someFunction` to execute, but 
// `otherFunction` not to execute. That can be fine, but it's a little weird
// and harder to reason about. I wouldn't generally do this.
// But also, it's rarely going to be a real problem.
Test.closureFunction { [weak self] parameter in
    self?.someFunction(parameter)
    self?.otherFunction(parameter)
}

A key point is that in both case, the code is "safe." It is not possible (in the absence of undefined behavior/unsafe code or Swift bugs) for self to ever be a dangling pointer or to change what object it points to.

  • Related