@autoclosure capture list propagation

Hello everyone!

The topics around @autoclosure and a possibility of retain cycle when used with them has been already brought up multiple times (references at the end of post if you are interested).

I'd like to ask about a single specific case - let's imagine this code sample:

class SDK {
  let producer: () -> String

  init(_ producer: @escaping @autoclosure String) {
    self.producer = producer
  }
}
// -- framework boundary --

class SDKWrapper {
  let sdk: SDK
  init() {
    self.sdk = SDK(self.makeString())
  }

  func makeString() -> String { "" }
}

As has been raised in other posts here, the consumer of SDK may have no clue that the call to self.makeString() isn't actually performed at that point and, what's worse, that this creates a retain cycle.

Once they learn about this, they may want to break it in the "usual" way, resulting in this code :

self.sdk = SDK( { [weak self] in 
  self?.makeString() ?? ""
}())

Which may look like it should fix the cycle (there is [weak self] capture after all), especially to less experienced developers.
In reality this doesn't fix the issue, since the synthesised autoclosure will still capture self strongly, just to pass it weakly to the inner closure.

So my questions are :

  • why isn't the capture list from the inner closure propagated to the autoclosure?
  • is this something that could be added to the language?

References :

3 Likes