What exactly happens when you capture `self` from a nested closure?

Recently I found some code snippets inside some class context which looked something like this:

// inside an object
func fooMethod() {
  ...
  {
    ...

    let closure = { [object = self] in 
      ...
    }
    ...
  }
}
  • What exactly happens here?
  • Is self captured twice?
  • Is self captured by the outer closure at all?
  • Is this even safe?
  • Would a theoretical reference count be increased by one or two?

Thanks in advance.

Inner closures only exist at the point where their containing closure runs. They capture from the scope that applies to their containing closure. Therefore:

Whatever the value of self is in the outer closure is captured into the inner one. This may cause a capture in the outer closure if the name self could be captured.

Depends what you mean by the word “captured”. Both closure blocks will have the same value in their scopes, yes, and so the closure context for the inner closure will contain a reference to self from the outer closure.

There must be a name self in the outer closure scope for this to work, yes. Again, inner closures are not constructed until the outer closure runs, so whatever is captured into the inner context must be in the outer context.

This question cannot be answered without the rest of the code. There is nothing inherently unsafe in this usage pattern, but you can build unsafe code out of anything.

This question is a bit under specified. Without the rest of the code, it’s impossible to know. In principle the ref count has to be incremented twice, once for each closure context, but depending on escaping and inlining it is possible that any number of refcount operations may occur.

3 Likes