> On Feb 19, 2017, at 6:45 AM, Matthew Johnson <matt...@anandabits.com> wrote:
> 
> 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.

The reason you have to explicitly say `self` is that `self` is the only 
variable you can implicitly reference. That's disabled in closures, so it's on 
the same footing as other variables.

Other than that, the only reason `self` is more likely to cause a loop is that, 
in common Cocoa patterns, you're more likely to be handing the closure to 
something owned by `self`. But that's not *necessarily* the case, it just 
happens to be true in many cases.

> 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.

I definitely agree this is a problem, but again, that doesn't mean `self` is 
the issue here. I think this is mainly because (a) the pattern isn't taught, 
and (b) there are some minor speed bumps in current Swift (like the inability 
to shadow `self` in a closure parameter list).

> 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`.

This doesn't work. The API designer doesn't know what's at the call site, and 
the user can't modify the API to suit the use.

> 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).

That seems like an ugly way to design APIs.

> 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.

That wouldn't work. The outer closure captures `self` from the context, and the 
inner closure captures `self` from the outer closure. The outer closure's 
capture would still be weak.

I really think this focus on `self` is counterproductive. I'm thinking we might 
be able to address this in a different way.

Let's look at your code sample:

        addAction(takesInt)

With the implicit `self`s made explicit, that would instead be:

        self.addAction(
                self.takesInt
        )

Now let's look at mine:

        alert.addAction(UIAlertAction(title: "Delete", style: .destructive) { _ 
in
                …
                viewController.performSegue(withIdentifier: "Cancel", sender: 
self)
        })
        …
                
        viewController.present(alert)

These have a similar pattern: The closure ends up being passed to a method on 
one of the variables it captures. Your example:

        self.addAction(
        ^ Closure gets passed to method on `self`
                self.takesInt
                ^ Closure captures `self`
        )

Mine:

        alert.addAction(UIAlertAction(title: "Delete", style: .destructive) { _ 
in
        ^ Closure passed to method on `alert`
                …
                viewController.performSegue(withIdentifier: "Cancel", sender: 
self)
                ^ Closure captures `viewController`
        })
        …
                
        viewController.present(alert)
        ^ `alert` passed to `viewController`

This seems like something the compiler—or perhaps a static analyzer?—could warn 
about. And it wouldn't target `self` specifically, or silently change the 
meaning of your code.

-- 
Brent Royal-Gordon
Architechies

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

Reply via email to