<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body dir="auto"><div><span style="background-color: rgba(255, 255, 255, 0);">I want to thank everyone who commented on the first draft of this proposal. I continue to believe the basic idea is a good one but there were a number of problems with the details of the design presented in that draft. They key insight that led to this new design is the idea of using a sigil at the usage site to establish different semantics than usual for guarded </span><span style="background-color: rgba(255, 255, 255, 0);">closures. Special thanks to those of you who focused my attention on the call site.</span></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br>This new draft presents a much stronger, more general design that I hope addresses all of the concerns expressed about the previous draft.<br><br><br># Guarded Closures and `@guarded` arguments<br><br>* Proposal: [SE-NNNN](NNNN-selfsafe.md)<br>* Authors: [Matthew Johnson](<a href="https://github.com/anandabits" dir="ltr" x-apple-data-detectors="true" x-apple-data-detectors-type="link" x-apple-data-detectors-result="0">https://github.com/anandabits</a>)<br>* Review Manager: TBD<br>* Status: **Awaiting review**<br><br>## Introduction<br><br>Some APIs have what we might call weak callback semantics. This design is common in many of Apple's newer callback APIs. These APIs greatly reduce the possibility of unintentional reference cycles / leaks by guaranteeing that they do not extend the lifetime of the observation target.</span></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br></span></div><div><span style="background-color: rgba(255, 255, 255, 0);">It is possible to implement APIs with this contract in Swift today, but the is don't feel very natural to either library implementers, or more importantly library users. It should be possible to design an API with this important semantic contract that feels natural to use (and implement) in Swift.<br><br>This proposal introduces guarded closures (prefixed with a `?` sigil). Closures that use this sigil default to capturing any references as `weak` and guard the invocation to be a no-op if any of the guarded references have been released before it is invoked. If no captured references have been invoked, they are upgraded to strong references for the duration of the invocation. This provides syntactic sugar for a very common pattern in Swift code (sometimes called the weak self / strong self dance).<br><br>It also introduces `@guarded` as an annotation that can be used on parameters of function type. Users are required to use a guarded closure when providing an argument for a `@guarded` parameter. This annotation is will be used in cases where it is *usually* (not always) wrong for the argument to extend the lifetime of objects that might be captured by the arguemnt it is given. This is a generalization of weak callback semantics that this proposal</span><span style="background-color: rgba(255, 255, 255, 0);"> calls guarded callback semantics.</span></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br>An early version of this proposal was discussed in the thread: [`@selfsafe`: a new way to avoid reference cycles](<a href="https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170213/032374.html" dir="ltr" x-apple-data-detectors="true" x-apple-data-detectors-type="link" x-apple-data-detectors-result="1">https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170213/032374.html</a>)<br><br>## Motivation<br><br>Accidentally forgeting to use weak references is a common problem and can easily lead to reference cycles. Some APIs (such as many kinds of callbacks / notifications / observers) are best designed such that users cannot extend the lifetime of objects captured by the function argument they are given (unless the user explicitly goes out of their way to state their intent to do so).<br><br>For example, `UIControl.addTarget(_:action:for:) does not retain the target, thereby preventing users from making the mistake of using a closure with an accidental strong reference. We can do something similar in Swift:<br><br>```swift<br>// in the library:<br>func addTarget<T: AnyObject>(_ target: T, action: T -> Int -> Void) {<br> // store a weak reference to the target<br> // when the action is fired call ref.map{ action($0)(42) }<br>}<br><br>// in user code:<br>class C {<br> init() {<br> addTarget(self, action: C.takesInt)<br> }<br><br> func takesInt(_ i: Int) {}<br>}<br>```<br><br>Both the library and the caller have to deal with a lot of details and boilerplate that we would prefer to avoid. The natural design in Swift would be to simply take an action function. Unfortunately if we do that we run into a problem:<br><br>```swift<br>// in the library<br>func addAction(_ f: Int -> Void) {<br> // store a strong ref to f, which might include a strong ref to a captured self<br> // later when the action is fired call f(42)<br>}<br><br>// in user code<br>class C {<br> init() {<br> addAction(takesInt)<br> // oops! should have been: addAction{ [weak self] self?.takesInt($0) }<br> }<br><br> func takesInt(_ i: Int) {}<br>}<br>```<br><br>Here the syntax is much nicer, but unfortunately we have unintentionally extended the lifetime of `self`. The burden of ensuring `self` is not captured or is captured weakly falls on users of the library.<br><br>It would very nice if it were possible to design an API that has weak or guarded callback semantics while still acheiving the more concise and Swifty syntax.<br><br>## Proposed solution<br><br>This proposal introduces guarded closures and the `@guarded` annotation for arguments of function type.<br><br>### Guarded Closures<br><br>A guarded closure can be created in two ways, both of which use the `?` sigil. The `?` sigil was selected because of it's relationship to `Optional` and to optional chaining.<br><br>First, the guarded captures are weak references with `Optional` type.<br><br>Second, anything following a `?` in an optional chain may or may not be invoked. The same is true of a guarded closure: its contents may or may not be executed when it is called by code that holds a reference to it. If any of the guarded captures have been released it immediately becomes a no-op that returns `()` or `nil`.<br><br>#### Bound instance methods<br><br>A guarded closure may be created by prefixing a bound instance method reference with the `?` sigil:<br><br>```swift<br>let guarded = ?myObject.myMethod<br><br>// desugars to:<br>let guarded = { [weak myObject] in<br> guard let myObejct = myObject else {<br> return<br> }<br> myObject.myMethod()<br>}<br>```<br><br>#### Inline closures<br><br>A guarded closure may be created by prefixing an inline closure with the `?` sigil:<br><br>```swift<br>let guarded = ?{ flag in<br> flag ? someMethod() : someOtherMethod()<br>}<br><br>// desugars to:<br>let guarded = { [weak self] flag in<br> guard let strongSelf = self else {<br> return<br> }<br><br> flag ? strongSelf.someMethod() : strongSelf.someOtherMethod()<br>```<br><br>#### Multiple guarded captures<br><br>```swift<br>let viewController = getAViewControllerOwnedElsewhere()<br>let guarded = ?{ flag in<br> flag ? someMethod() : someOtherMethod()<br> viewController.doSomething()<br>}<br><br>// desugars to:<br>let guarded = { [weak self, weak viewController] flag in<br> guard let strongSelf = self, viewController = viewController else {<br> return<br> }<br><br> flag ? strongSelf.someMethod() : strongSelf.someOtherMethod()<br> viewController.doSomething()<br>```<br><br>#### Capture lists<br><br>As with any closure, a guarded closure may specify a capture list. Any captures specified in the capture list are not guarded and instead adopt the semantics specified by the capture list. This includes `weak` captures.<br><br>```swift<br>let guarded = ?{ [weak object, unowned otherObject, strong thirdObject] in<br> aMethodOnSelf()<br> object?.doSomething(with: otherObject, and: thirdObject)<br>}<br><br>// desugars to:<br>let guarded = ?{ [weak self, weak object, unowned otherObject, strong thirdObject] in<br> guard let strongSelf = self else {<br> return<br> }<br><br> strongSelf.aMethodOnSelf()<br> object?.doSomething(with: otherObject, and: thirdObject)<br>}<br>```<br><br>#### Self reference in escaping guarded closures<br><br>Guarded closures do not extend the lifetime of any objects unless a `strong` capture is specified in the capture list. Because this is the case users are not required to explicitly reference self using the `self.` prefix inside a guarded closure.<br><br>#### Return values<br><br>Guarded closures bail immediately when one of the guarded captures has been released. This proposal handles return values by requiring them to always return `Void` or `Optional`. If the inferred type of the closure is not `Optional` it is wrapped in `Optional`. `Optional` returning guarded closures return `nil` when any of the guarded captures has been released. `Void` returning guarded closures simply become a no-op after the first guarded capture has been released.<br><br>### `@guarded`<br><br>This proposal introduces the ability for arguments of function type to be annotated with the `@guarded` attribute. When `@guarded` is used the caller must supply a guarded or non-capturing closure as an argument. This ensures that users of the API think very carefully before providing an argument that captures a strong reference, and requires them to explictly state their intent when they wish to do so.<br><br>Because `@guarded` only makes sense in the context of an `@escaping` closure it implies `@escaping`. Users do not need to specify both annotations.<br><br>This allows a library to do something like the following (a simplified version of a hypothetical future method on `UIControl`):<br><br>```swift<br> class UIControl {<br> var action: () -> Void = {}<br><br> func setAction(_ action: @guarded () -> Void) {<br> self.action = action<br> }<br><br> func fireAction() {<br> action()<br> }<br> }<br>```<br><br>This API can be used as follows:<br><br>```swift</span></div><div><span style="background-color: rgba(255, 255, 255, 0);">myControl.setAction(?myMethod) // great! no reference cycle.</span></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br></span></div><div><span style="background-color: rgba(255, 255, 255, 0);">let f = ?myMethod</span></div><div><span style="background-color: rgba(255, 255, 255, 0);">// ok: compiler knows f is guarded</span></div><div><span style="background-color: rgba(255, 255, 255, 0);">secondControl.setAction(f)<br><br></span></div><div><span style="background-color: rgba(255, 255, 255, 0);">// otherObject should live as long as the closure might be invoked</span></div><div><span style="background-color: rgba(255, 255, 255, 0);">myOtherControl.setAction ?{ [strong otherObject] in<br> // `self.doSomethingElse()` is not required.<br> // The compiler knows we cannot create a reference cycle here.<br> doSomethingElse()</span></div><div><span style="background-color: rgba(255, 255, 255, 0);"> otherObject.doSomething()</span></div><div><span style="background-color: rgba(255, 255, 255, 0);">}</span></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br></span></div><div><span style="background-color: rgba(255, 255, 255, 0);">// ok: this is not a guarded closure but it only captures by value, no references are captured</span></div><div><span style="background-color: rgba(255, 255, 255, 0);">let name = "Swift"</span></div><div><span style="background-color: rgba(255, 255, 255, 0);">anotherControl.setAction {</span></div><div><span style="background-color: rgba(255, 255, 255, 0);"> print("hello \(name)")</span></div><div><span style="background-color: rgba(255, 255, 255, 0);">}</span></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br></span></div><div><span style="background-color: rgba(255, 255, 255, 0);">// error: closure capturing a reference must be guarded</span></div><div>yetAnotherControl.setAction {</div><div> anObjectReference.method()</div><div>}</div><div><span style="background-color: rgba(255, 255, 255, 0);"><br></span></div><div><span style="background-color: rgba(255, 255, 255, 0);">// error: strong capture not allowed without capture list</span></div><div><span style="background-color: rgba(255, 255, 255, 0);">lastControl.setAction(myMethod)</span></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br></span></div><div><span style="background-color: rgba(255, 255, 255, 0);">// ok</span></div><div><span style="background-color: rgba(255, 255, 255, 0);">lastControl.setAction { [strong self] in self.myMethod() }<br>```<br><br>#### Externally captured closures</span></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br></span></div><div><span style="background-color: rgba(255, 255, 255, 0);">The previous examples all show inline closures and method references. We also need to consider functions that might be received as an argument or stored in an instance, static, or global property.</span></div><div><br></div><div>It is obvious that a function should be allowed to forward a function when it is received as an argument annotated with `@guarded`. In all other cases directly forwarding a closure is not allowed. It can however be wrapped in a guarded closure pretty concisely:</div><div><br></div><div>```swift</div><div>func foo(_ f: @guarded () -> Void) {</div><div> // ok</div><div> otherFuncTakingGuardedClosure(f)</div><div>}</div><div><br></div><div><div><span style="background-color: rgba(255, 255, 255, 0);">func foo(_ f: () -> Void) {</span></div><div><span style="background-color: rgba(255, 255, 255, 0);"> // error: `f` is not guarded</span></div><div><span style="background-color: rgba(255, 255, 255, 0);"> foo(f)</span></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br></span></div><div><span style="background-color: rgba(255, 255, 255, 0);"> // ok: explicitly wrapped </span></div><div><span style="background-color: rgba(255, 255, 255, 0);"> foo ?{ [strong f] in f() }</span></div><div><span style="background-color: rgba(255, 255, 255, 0);">}</span></div></div><div>```</div><div><span style="background-color: rgba(255, 255, 255, 0);"><br>## Detailed design<br><br>The primary detail that isn't discussed above is that the implementation should take advantage of the semantics of guarded closures where possible. For example, as soon as it identifies that any of the references captured with guarded semantics has been released the entire context should be released and only a no-op stub should remain as long as the closure is referenced.<br><br>One additional detail: it should be possible to pass a guarded closure to an API that does not require a guarded closure unless there is a strong implementation reason to prohibit this.</span></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br>Apple API annotation<br><br>## Source compatibility<br><br>This is a purely additive change to the language. </span></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br></span></div><div><span style="background-color: rgba(255, 255, 255, 0);">However, if it is introduced it is likely that a number of libraries would adopt `@guarded` semantics. This would be a moderately complex breaking change for users of the library that would impact a lot of user code. Automated migration would theoretically be possible but extremely non-trivial and therefore likely not provided.</span></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br></span></div><div><span style="background-color: rgba(255, 255, 255, 0);">I haven't done a careful review, but there may be some APIs that are part of <a href="http://Swift.org">Swift.org</a> which for which it might make sense to adopt guarded callback semantics.</span></div><div><br></div><div><span style="background-color: rgba(255, 255, 255, 0);">## Effect on ABI stability</span></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br></span></div><div><span style="background-color: rgba(255, 255, 255, 0);">I am not an ABI expert so takes these comments with a big grain of salt (expert input is welcome!).<br><br>This is a purely additive change. However, it is possible that guarded closures might have different ABI in order to take advantage of early release of the context.</span></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br></span></div><div><span style="background-color: rgba(255, 255, 255, 0);">It is also possible that an Objective-C annotation could be provided that would allow Apple frameworks to adopt guarded callback semantics when used from Swift. I am not sure what impact this would have if it happened.</span></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br>## Effect on API resilience<br><br>Migrating an API from `@escaping` to `@guarded` is a breaking change.</span></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br>## Future directions</span></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br></span></div><div><span style="background-color: rgba(255, 255, 255, 0);">It would be very useful to expose an `isAlive` property on guarded closures that would allow libraries to detect whether the guarded references are all still alive or not. This would allow libraries to discard guarded closures which have become no-ops.<br><br>It might be interesting to allow users to specify a default return value that is used after one of the guarded references has been released. If this were possible we would not have to restrict guarded closures to `Void` and `Optional` return types.</span></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br>## Alternatives considered<br><br>We could leave the situation as-is. This would be very unfortunate. Some APIs are better with weak or guarded capture semantics. It should be easier to design and use these APIs in Swift.</span></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br>I considered various ways of exposing API on function objects. One possibility is to allow libraries to access the whole closure context or parts of it directly along with the unbound function. Another is to allow a library to derive a new closure with a modified self capture that has guarded semantics.</span></div><div><span style="background-color: rgba(255, 255, 255, 0);"> </span></div><div><span style="background-color: rgba(255, 255, 255, 0);">These solutions are more complex than necessary and unnecessarily expose implementation details.</span></div><div><br><div>Sent from my iPad</div></div></body></html>