Sent from my iPad

On Feb 19, 2017, at 2:15 AM, Brent Royal-Gordon <br...@architechies.com> wrote:

>> On Feb 18, 2017, at 5:24 PM, Matthew Johnson via swift-evolution 
>> <swift-evolution@swift.org> wrote:
>> 
>> 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.
> 
> Both of these mechanisms are weirdly ad hoc. They involve the callee assuming 
> things about the caller that are not necessarily correct—in particular, that 
> the caller's `self` is going to, directly or indirectly, hold a strong 
> reference to the callee's `self`.

After I sent this draft last night I realized we can eliminate the 
`withWeakSelf` property on functions and just have the compiler perform the 
conversion when the argument is passed.  This just requires restricting the 
`@safeself` annotation to `Void` and `Optional` returning functions.  This will 
prevent unintended uses of that property and keep more of the implementation 
details hidden, so let's just focus on `@selfsafe`.  (Also note: this name is a 
straw man, I would like to find something better)

Here's the logic that resulted in this idea:

1. Swift *already* acknowledges that it is far easier to create a reference 
cycle through captured strong references to `self` than any other way.  This is 
why you have to explicitly say `self.` in escaping closures.

2. There are *already* APIs which are designed to capture an object weak and 
then call a method on it if it's still around when an event occurs.  In fact 
this has been a trend in Apple's newer APIs.  Users are able to learn the 
semantics of these APIs without a problem.  In fact, users like the, because 
they solve a real problem by ensuring that3.  object lifetime is not extended 
when using them.

3. Swift libraries don't tend to design APIs with weak callback semantics, 
probably because they are currently syntactically heavy for both users and 
libraries.  This is true even when weak callback semantics would be beneficial 
for users and help prevent leaks (which can lead to crashes and other badly 
behaved apps).

4. There have been several ideas proposed to make weak capture easier to do on 
the call side but they haven't gone anywhere.  The syntactic savings aren't 
that significant for callers and the burden is still on callers to get it right.

All of these facts indicate to me that it should be possible to design an API 
with weak callback semantics that does not impose a significant syntactic 
burden on callers.  I am not as concerned about a possible syntactic burden on 
libraries, but clients should be able to pass `myMethod` or `{ myMethod(); 
myOtherMethod() }` and have it "just work".  A small decoration like the `&` 
required for `inout` would be acceptable, but not much more than that.

> 
> For instance, suppose you've read too many design pattern books, and you're 
> using the Command Pattern:
> 
>    class DeleteRecordCommand: Command {
>        let record: Record
>        
>        func execute(with viewController: RecordViewController) {
>            let alert = UIAlertController(title: "Really delete?", 
> preferredStyle: .alert)
>            
>            alert.addAction(UIAlertAction(title: "Delete", style: 
> .destructive) { _ in
>                self.record.delete()
>                viewController.performSegue(withIdentifier: "Cancel", sender: 
> self)
>            })
>            alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
>            
>            viewController.present(alert)
>        }
>    }
> 
> Now, imagine that the `UIAlertAction` initializer used `@selfsafe`, hoping to 
> prevent incorrect use of `self`. Well, that would be totally wrong in this 
> case—it would weaken `self`, which *needs* to be strong, and leave 
> `viewController` strong, when it's creating a retain cycle. `@selfsafe` 
> didn't prevent a bug—it introduced another one, without any visible sign of 
> it in the code.

You bring up a very important point.  I was going to include a section on "what 
if users actually need a strong reference" and simply forgot to add that before 
I sent it out.

If users actually need a strong reference there are ways to handle that.  
First, if it is likely that a user might want self to be captured strong the 
API might choose to not use `@selfsafe`.  If they *do* choose to use it the 
could also add an overload that does not use it and is disambiguated using some 
other means (base name or argument label).  Finally, users can always add an 
extra closure wrapper {{ self.myMethod() }} to bypass the API's weak callback 
semantics.  Here, `self` is captured by the inner closure rather than the the 
outer closure and the API only converts strong `self` references in the outer 
closure.

All of this aside, I think you make a great point that in the design I proposed 
it is possible for users to intend a strong reference and have it converted 
without any sign of that at the call site.  What if we required a sigil at the 
call site for `selfsafe` like we do for `inout`?  This would make it clear at 
the call site that the lifetime of `self` is not extended by the closure and it 
is only invoked if `self` is still around when an event occurs.  I'm going to 
try to think of a good sigil to use for this.

> 
> *The problem is not `self`.* The problem is that you need to pay attention to 
> memory management in Swift. And that's a tradeoff that's baked into the 
> language.

Yes, and it's also something that we strive to handle automatically where 
possible and make as easy as possible to do correctly.  I am *very* careful 
about capturing strong references to `self` and still make an accidental 
mistake every once in a while.  

I am very confident that enabling the class of weak callback APIs I have 
described is a good idea (regardless of how the end result is designed).  This 
would be a powerful tool in our tool belts and help make correct resource 
lifetime management easier than it is today.

> 
> -- 
> Brent Royal-Gordon
> Architechies
> 

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to