This places the burden on users. The library is not able to offer a guarantee.
Sent from my iPad > On Feb 19, 2017, at 1:20 AM, Derrick Ho <wh1pch...@gmail.com> wrote: > > What wrong with [unowned self] >> On Sat, Feb 18, 2017 at 11:01 PM Daniel Duan via swift-evolution >> <swift-evolution@swift.org> wrote: >> This reminded me of an idea I had long time ago which will have a similar >> effect: add a way to disable implicit captures for closures. FWIW. >> >> > On Feb 18, 2017, at 5:24 PM, Matthew Johnson via swift-evolution >> > <swift-evolution@swift.org> wrote: >> > >> > # `@selfsafe`: a new way to avoid reference cycles >> > >> > * Proposal: [SE-NNNN](NNNN-selfsafe.md) >> > * Authors: [Matthew Johnson](https://github.com/anandabits) >> > * Review Manager: TBD >> > * Status: **Awaiting review** >> > >> > ## Introduction >> > >> > This proposal introduces the `@selfsafe` function argument attribute which >> > together with a `withWeakSelf` property on values of function type. >> > Together these features enable library authors to create APIs can be >> > statically verified to never extend the lifetime of the `self` a function >> > they take may have captured. This is accomplished by allowing the library >> > implementation convert the function to a nearly identical function that is >> > guaraneteed to have a `weak` capture of `self` and be a no-op after `self` >> > is released. >> > >> > Swift-evolution thread: []() >> > >> > ## Motivation >> > >> > Accidentally forgeting to use weak references is a common problem and can >> > easily lead to reference cycles. Some APIs are best designed such that >> > users *cannot* extend the lifetime of `self` by escaping a closure that >> > happens to strongly capture `self`. 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: >> > >> > ```swift >> > // in the library: >> > func addTarget<T: AnyObject>(_ target: T, action: T -> Int -> Void) { >> > // store a weak reference to the target >> > // when the action is fired call ref.map{ action($0)(42) } >> > } >> > >> > // in user code: >> > class C { >> > init() { >> > addTarget(self, action: C.takesInt) >> > } >> > >> > func takesInt(_ i: Int) {} >> > } >> > ``` >> > >> > 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: >> > >> > ```swift >> > // in the library >> > func addAction(_ f: Int -> Void) { >> > // store a strong ref to f, which might include a strong ref to a >> > captured self >> > // later when the action is fired call f(42) >> > } >> > >> > // in user code >> > class C { >> > init() { >> > addAction(takesInt) >> > // oops! should have been: addAction{ [weak self] self?.takesInt($0) } >> > } >> > >> > func takesInt(_ i: Int) {} >> > } >> > ``` >> > >> > 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 captured weakly falls on users of the library. >> > >> > It would very nice if it were possible to design an API that has weak >> > capture semantics while still acheiving the more concise and Swifty syntax. >> > >> > ## Proposed Solution >> > >> > This proposal introduces a read-only property on all function types: >> > `withWeakSelf` as well as a `@selfsafe` function argument annotation. >> > (This name for the annotation is a strawman - I would love to hear better >> > ideas) >> > >> > ### `withWeakSelf` >> > >> > `withWeakSelf` can be imagined as a property declared like the following: >> > >> > ```swift >> > extension T -> Void { >> > var withWeakSelf: T -> Void { return // compiler magic } >> > } >> > extension T -> U { >> > var withWeakSelf: T -> U? { return // compiler magic } >> > } >> > extension T -> U? { >> > var withWeakSelf: T -> U? { return // compiler magic } >> > } >> > ``` >> > >> > It returns a closure that is identical to itself in every respect except >> > four: >> > 1. If the context includes a strong or unowned `self` capture, that is >> > converted to a weak capture. >> > 2. If the function returns a non-`Void`, non-`Optional` type the return >> > type is wrapped in an `Optional`. >> > 3. The function returned by `withWeakSelf` is a no-op after `self` has >> > been released. If the return type is non-`Void`, the function returns >> > `nil` after `self` has been released. >> > 4. Any addtional non-`self` context is released as soon as possible after >> > `self` is released. >> > >> > From these rules, it follows that `withWeakSelf` can be a no-op unless >> > `self` is captured strong or unowned or the return type is non-`Void` and >> > non-`Optional`. >> > >> > Important note: any additional context the closure requies beyond `self` >> > continues to be captured as it was originally. >> > >> > ### `@selfsafe` >> > >> > When a function argument is annotated with `@selfsafe` two rules are in >> > effect for the implementation: >> > 1. The argument *is not* allowed to escape the function. >> > 2. The closure returend by `withWeakSelf` *is* allowed to escape the >> > function. >> > >> > There should also be a warning if `argument.withWeakSelf` *does not* >> > escape the function in any code paths (because the `@selfsafe` annotation >> > is meaningless if it does not escape). >> > >> > This allows a library to do something like the following (a simplified >> > version of a hypothetical future method on `UIControl`): >> > >> > ```swift >> > class UIControl { >> > >> > var action: () -> Void >> > >> > func setAction(_ action: @selfsafe () -> Void) { >> > self.action = action.withWeakSelf >> > } >> > } >> > ``` >> > >> > In the previous example the function declares the argument with the >> > `@selfsafe` annotation. The compiler verifies that the `action` argument >> > does not escape `setAction` and that `action.withWeakSelf` *does* escape. >> > Because these verifications guarantee that the lifetime of `self` cannot >> > be exteneded by calling `setAction`, the compiler can allow users to omit >> > `self` if this function is called with a closure. >> > >> > ```swift >> > class UIViewController { >> > func viewDidLoad() { >> > // misc setup >> > myControl.setAction(doSomething) // great! no problem creating a >> > reference cycle. >> > myOtherControl.setAction { >> > // `self.doSomethingElse()` is not required. >> > // The compiler knows we cannot create a reference cycle here. >> > doSomethingElse() >> > } >> > } >> > func doSomething() {} >> > func doSomethingElse() {} >> > } >> > ``` >> > >> > ## Detailed design >> > >> > TBD - If anyone can think of additional details that need to be spelled >> > out please provide feedback in this thread. >> > >> > ## Source compatibility >> > >> > No effect. This is a purely additive change. >> > >> > ## Effect on ABI stability >> > >> > No effect. This is a purely additive change. >> > >> > ## Effect on API resilience >> > >> > No effect. This is a purely additive change. >> > >> > >> > ## Alternatives considered >> > >> > I considered various ways of exposing the entire captured context or the >> > captured `self` reference directly as properties of function types, along >> > with access to an unbound version of the function. This turns out to be >> > far more complex than necessary to solve the problem this proposal aims to >> > solve and doesn't enable any interesting use cases I have thought of. >> > >> > _______________________________________________ >> > swift-evolution mailing list >> > swift-evolution@swift.org >> > https://lists.swift.org/mailman/listinfo/swift-evolution >> >> _______________________________________________ >> swift-evolution mailing list >> swift-evolution@swift.org >> https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution