1

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

Jakub Gawecki
  • 801
  • 2
  • 6
  • 14
  • "I think that we may still want to execute an operation from within the escaping closure's scope while self is already nil". Well if you want to do that, then you obviously shouldn't unwrap self like that. But most of the time, if `self` is nil, it doesn't make sense to continue executing the code anymore, does it? – Sweeper Dec 05 '22 at 19:15
  • "But most of the time", this part of your comment makes me believe that question is right and there are cases when that may happen and we want to continue on execution. – Jakub Gawecki Dec 05 '22 at 19:17
  • 1
    Related? https://stackoverflow.com/q/45327236/1187415 – Martin R Dec 05 '22 at 19:23
  • It's not just aesthetical though: if you say `guard let self = self else { return }` then no statement below it will need to be evaluated if the `self` is nil. So if you do anything other than `self?...` (e.g. calculating something, accessing some member etc) there's a practical difference between the two – timbre timbre Dec 05 '22 at 19:23
  • Note that you can now say `guard let self else { return }`. No assignment needed – matt Dec 05 '22 at 19:26

2 Answers2

3

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.

Duncan C
  • 128,072
  • 22
  • 173
  • 272
0

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.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • Thanks for the clarifying example. Note that as Matt points out in his comment, in the latest version of Swift, you can say `guard let self` rather than `guard let self = self` – Duncan C Dec 06 '22 at 00:21