The scope of an unwrapped optional in an optional chain

What is the scope/lifetime of an optional that successfully unwraps in an optional chain?

For example:

let closure = { [weak self] in
  self?.someMethod(with: self!.someProperty)
}

My understanding is that this executes safely at runtime because the optional chain evaluates from left to right, so self? unwraps in order to call someMethod(), and since it is retained while the method is called, self! is then safely unwrapped in order to pass in the argument. If self deallocates before the self? expression is evaluated, someMethod() isn't called and the rest of the statement isn't evaluated.

If, however, there is another value/object involved in the chain, like another property on self:

let closure = { [weak self] in
  self?.someProperty.someMethod(with: self!.someOtherProperty)
}

Is this still a "safe" statement at runtime?

Once self? is unwrapped, is the strong reference to the unwrapped self immediately released once someProperty is resolved, or is it held while someProperty.someMethod() is called?

Is there a chance that self? successfully unwraps, someProperty is retained, self goes out of scope and is deallocated, and self! now fails when someMethod() is executed?

Or is this previous statement equivalent to:

let closure = { [weak self] in
  if let sself = self {
    sself.someProperty.someMethod(with: sself.someOtherProperty)
  }
}

Yes, that is a possibility; successfully loading a non-nil value from the weak capture of self does not extend the underlying object's lifetime beyond the code that immediately uses it.

Usually this is okay because you're not actually racing with the final release of the object. It's not really a good idea to set up a closure that races that way!

1 Like

There was a related discussion at Confirming order of operations , where Joe Groff said:

This isn't guaranteed. Releases may be optimized to happen earlier than this, to any point after the last formal use of the strong reference. ...

1 Like

Thanks @John_McCall and Martin — just trying to get crystal clear now, so that I can remove all doubt.

My sense is that this scenario could be slightly different than @Joe_Groff addressed, in that he's saying an optional from the left-hand side of an assignment isn't guaranteed to be retained while the right-hand side is evaluated. As krilnon showed in the emitted SIL, while the sequence of retains and releases allows this to work (or may sometimes work) today, it isn't guaranteed.

So, is the scenario different if both references to self appear in the same method call, as in my example?

It seems like the answer hangs on what Joe calls "the last formal use of the strong reference".

Meaning, assuming self?.someProperty constitutes a formal use as a direct method call to the getter on self, can self?.someProperty.someMethod() also constitute a formal use of self because it appears in the same expression?

Are there any references I can consult?

Thanks for the help!

I don't know that we'd guarantee that it would work even in the same method call. Now, for all practical purposes, if the method call can't be devirtualized and optimized then there's no way it wouldn't actually work, but I think we'd always encourage people to do a single load and check instead of trying to rely on that kind of analysis.

1 Like