$self


(Paul Jack) #1

So previously there were a few threads on the "strong self/weak self
dance" but they didn't seem to get anywhere. For instance:

https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160201/008713.html
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160215/010759.html
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160208/009972.html

...and possibly others.

I'd like to propose something even easier (and more specific) than all
of the above discussions. Specifically, I'd like to introduce a new
automagic closure variable, $self, whose presence in a closure would
cause that closure to weakly capture self in a safe manner.

As a concrete example, let's imagine a UIViewController for a login
form. Under this proposal, the following code:

func viewDidLoad() {
    self.loginForm.onSubmit = {
         let f = $self.loginForm
         $self.startLoginRequest(email:f.email.text, pwd:f.pwd.text)
    }
}

...would be treated by the compiler as equivalent to:

func viewDidLoad() {
    self.loginForm.onSubmit = {
         [weak self] in
         if let selfie = self {
             let f = selfie.loginForm
             selfie.startLoginRequest(email:f.email.text,
             pwd:f.pwd.text)
         }
    }
}

Note the "if let" there: If self no longer exists, the closure does not
execute at all, but if self does exist, then it exists for the entirety
of the execution of the closure (ie, self won't vanish as a side-effect
of some statement in the closure.) I think these semantics obey the
principle of least surprise; $self can be treated by the developer as a
strong reference.

However, that does mean that $self can only be used in a closure that's
(a) Void or (b) Optional. In the latter case, returning nil when self
doesn't exist seems like reasonable/expected behavior.

It would be a compile-time error to use both $self and normal self in
the same closure.

I'd like to keep this simple, meaning $self always does the above and
nothing else. So, if you need an unowned self, you still need the
original syntax; if your closure needs a non-Optional return type, you
still need the original syntax; etc.

Thoughts?

-Paul


(Chris Lattner) #2

This is an additive proposal, so we’d prefer for you to hold off until we get the core work for Swift 4 stage 1 under control. We need to focus on making sure ABI and Source Stability takes priority.

-Chris

···

On Sep 28, 2016, at 4:05 PM, Paul Jack via swift-evolution <swift-evolution@swift.org> wrote:

So previously there were a few threads on the "strong self/weak self
dance" but they didn't seem to get anywhere. For instance:

https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160201/008713.html
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160215/010759.html
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160208/009972.html

...and possibly others.

I'd like to propose something even easier (and more specific) than all
of the above discussions. Specifically, I'd like to introduce a new
automagic closure variable, $self, whose presence in a closure would
cause that closure to weakly capture self in a safe manner.


(Jay) #3

It could potentially be a breaking change if the default for @escaping
closures were made to be weak-capturing.

Since the weak-capturing pattern is only really desirable for @escaping
closures, and (I think) it would be the usual preference, could @escaping
also imply weak-capturing for all references (not just self)? Then there
would be another syntax for strong-capturing-escaping closures.
Non-escaping closures could a) strongly capture references; or b) existing
strong references stay strong and weak ones stay weak, meaning no
ref-counts need to change at all when passing them.

···

On Thu, 29 Sep 2016 at 00:06 Paul Jack via swift-evolution < swift-evolution@swift.org> wrote:

So previously there were a few threads on the "strong self/weak self
dance" but they didn't seem to get anywhere. For instance:

https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160201/008713.html

https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160215/010759.html

https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160208/009972.html

...and possibly others.

I'd like to propose something even easier (and more specific) than all
of the above discussions. Specifically, I'd like to introduce a new
automagic closure variable, $self, whose presence in a closure would
cause that closure to weakly capture self in a safe manner.

As a concrete example, let's imagine a UIViewController for a login
form. Under this proposal, the following code:

func viewDidLoad() {
    self.loginForm.onSubmit = {
         let f = $self.loginForm
         $self.startLoginRequest(email:f.email.text, pwd:f.pwd.text)
    }
}

...would be treated by the compiler as equivalent to:

func viewDidLoad() {
    self.loginForm.onSubmit = {
         [weak self] in
         if let selfie = self {
             let f = selfie.loginForm
             selfie.startLoginRequest(email:f.email.text,
             pwd:f.pwd.text)
         }
    }
}

Note the "if let" there: If self no longer exists, the closure does not
execute at all, but if self does exist, then it exists for the entirety
of the execution of the closure (ie, self won't vanish as a side-effect
of some statement in the closure.) I think these semantics obey the
principle of least surprise; $self can be treated by the developer as a
strong reference.

However, that does mean that $self can only be used in a closure that's
(a) Void or (b) Optional. In the latter case, returning nil when self
doesn't exist seems like reasonable/expected behavior.

It would be a compile-time error to use both $self and normal self in
the same closure.

I'd like to keep this simple, meaning $self always does the above and
nothing else. So, if you need an unowned self, you still need the
original syntax; if your closure needs a non-Optional return type, you
still need the original syntax; etc.

Thoughts?

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


(Chris Lattner) #4

It could potentially be a breaking change if the default for @escaping closures were made to be weak-capturing.

Ok, but source breaking changes need extreme justification. A primary goal of Swift 3 was to provide source compatibility going forward.

-Chris

···

On Sep 28, 2016, at 4:42 PM, Jay Abbott via swift-evolution <swift-evolution@swift.org> wrote:

Since the weak-capturing pattern is only really desirable for @escaping closures, and (I think) it would be the usual preference, could @escaping also imply weak-capturing for all references (not just self)? Then there would be another syntax for strong-capturing-escaping closures. Non-escaping closures could a) strongly capture references; or b) existing strong references stay strong and weak ones stay weak, meaning no ref-counts need to change at all when passing them.

On Thu, 29 Sep 2016 at 00:06 Paul Jack via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
So previously there were a few threads on the "strong self/weak self
dance" but they didn't seem to get anywhere. For instance:

https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160201/008713.html
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160215/010759.html
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160208/009972.html

...and possibly others.

I'd like to propose something even easier (and more specific) than all
of the above discussions. Specifically, I'd like to introduce a new
automagic closure variable, $self, whose presence in a closure would
cause that closure to weakly capture self in a safe manner.

As a concrete example, let's imagine a UIViewController for a login
form. Under this proposal, the following code:

func viewDidLoad() {
    self.loginForm.onSubmit = {
         let f = $self.loginForm
         $self.startLoginRequest(email:f.email.text, pwd:f.pwd.text)
    }
}

...would be treated by the compiler as equivalent to:

func viewDidLoad() {
    self.loginForm.onSubmit = {
         [weak self] in
         if let selfie = self {
             let f = selfie.loginForm
             selfie.startLoginRequest(email:f.email.text,
             pwd:f.pwd.text)
         }
    }
}

Note the "if let" there: If self no longer exists, the closure does not
execute at all, but if self does exist, then it exists for the entirety
of the execution of the closure (ie, self won't vanish as a side-effect
of some statement in the closure.) I think these semantics obey the
principle of least surprise; $self can be treated by the developer as a
strong reference.

However, that does mean that $self can only be used in a closure that's
(a) Void or (b) Optional. In the latter case, returning nil when self
doesn't exist seems like reasonable/expected behavior.

It would be a compile-time error to use both $self and normal self in
the same closure.

I'd like to keep this simple, meaning $self always does the above and
nothing else. So, if you need an unowned self, you still need the
original syntax; if your closure needs a non-Optional return type, you
still need the original syntax; etc.

Thoughts?

-Paul
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto: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


(Sean Heber) #5

Pretty sure this is all way out of scope, but something about this made me think about this idea (which maybe isn't unique or is maybe even unworkable), but imagine something like where a new capture type is added such as "required" (for lack of another name right now). And the way this works is similar to unowned, but it makes the entire closure "weak" in such a way that the moment any of the required captures go nil, any references to that closure instance also effectively become nil.

So modifying the example:

func viewDidLoad() {
    self.loginForm.onSubmit = {[required self]
         let f = self.loginForm
         self.startLoginRequest(email:f.email.text, pwd:f.pwd.text)
    }
}

So in this case, "required self" means self is effectively "unowned" but any references to this closure would have to be weak optional like: weak var onSubmit: (()->Void)? So that when the view controller gets deallocated, the closure goes with it and references become nil.

l8r
Sean

···

Sent from my iPad

On Sep 28, 2016, at 6:42 PM, Jay Abbott via swift-evolution <swift-evolution@swift.org> wrote:

It could potentially be a breaking change if the default for @escaping closures were made to be weak-capturing.

Since the weak-capturing pattern is only really desirable for @escaping closures, and (I think) it would be the usual preference, could @escaping also imply weak-capturing for all references (not just self)? Then there would be another syntax for strong-capturing-escaping closures. Non-escaping closures could a) strongly capture references; or b) existing strong references stay strong and weak ones stay weak, meaning no ref-counts need to change at all when passing them.

On Thu, 29 Sep 2016 at 00:06 Paul Jack via swift-evolution <swift-evolution@swift.org> wrote:
So previously there were a few threads on the "strong self/weak self
dance" but they didn't seem to get anywhere. For instance:

https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160201/008713.html
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160215/010759.html
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160208/009972.html

...and possibly others.

I'd like to propose something even easier (and more specific) than all
of the above discussions. Specifically, I'd like to introduce a new
automagic closure variable, $self, whose presence in a closure would
cause that closure to weakly capture self in a safe manner.

As a concrete example, let's imagine a UIViewController for a login
form. Under this proposal, the following code:

func viewDidLoad() {
    self.loginForm.onSubmit = {
         let f = $self.loginForm
         $self.startLoginRequest(email:f.email.text, pwd:f.pwd.text)
    }
}

...would be treated by the compiler as equivalent to:

func viewDidLoad() {
    self.loginForm.onSubmit = {
         [weak self] in
         if let selfie = self {
             let f = selfie.loginForm
             selfie.startLoginRequest(email:f.email.text,
             pwd:f.pwd.text)
         }
    }
}

Note the "if let" there: If self no longer exists, the closure does not
execute at all, but if self does exist, then it exists for the entirety
of the execution of the closure (ie, self won't vanish as a side-effect
of some statement in the closure.) I think these semantics obey the
principle of least surprise; $self can be treated by the developer as a
strong reference.

However, that does mean that $self can only be used in a closure that's
(a) Void or (b) Optional. In the latter case, returning nil when self
doesn't exist seems like reasonable/expected behavior.

It would be a compile-time error to use both $self and normal self in
the same closure.

I'd like to keep this simple, meaning $self always does the above and
nothing else. So, if you need an unowned self, you still need the
original syntax; if your closure needs a non-Optional return type, you
still need the original syntax; etc.

Thoughts?

-Paul
_______________________________________________
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


(Sean Heber) #6

Now that I think about this, wouldn't that be a better default behavior? All captures are this "required" type which means all closures are typed as optional. To override that behavior, you'd have to explicitly declare a weak or unowned capture instead and if you did that for all reference captures, the closure's type would be non-optional as they are now. Seems like that'd be safer. I'll shut up now.

l8r
Sean

···

Sent from my iPad

On Sep 28, 2016, at 7:25 PM, Sean Heber <sean@fifthace.com> wrote:

Pretty sure this is all way out of scope, but something about this made me think about this idea (which maybe isn't unique or is maybe even unworkable), but imagine something like where a new capture type is added such as "required" (for lack of another name right now). And the way this works is similar to unowned, but it makes the entire closure "weak" in such a way that the moment any of the required captures go nil, any references to that closure instance also effectively become nil.

So modifying the example:

func viewDidLoad() {
    self.loginForm.onSubmit = {[required self]
         let f = self.loginForm
         self.startLoginRequest(email:f.email.text, pwd:f.pwd.text)
    }
}

So in this case, "required self" means self is effectively "unowned" but any references to this closure would have to be weak optional like: weak var onSubmit: (()->Void)? So that when the view controller gets deallocated, the closure goes with it and references become nil.

l8r
Sean

Sent from my iPad

On Sep 28, 2016, at 6:42 PM, Jay Abbott via swift-evolution <swift-evolution@swift.org> wrote:

It could potentially be a breaking change if the default for @escaping closures were made to be weak-capturing.

Since the weak-capturing pattern is only really desirable for @escaping closures, and (I think) it would be the usual preference, could @escaping also imply weak-capturing for all references (not just self)? Then there would be another syntax for strong-capturing-escaping closures. Non-escaping closures could a) strongly capture references; or b) existing strong references stay strong and weak ones stay weak, meaning no ref-counts need to change at all when passing them.

On Thu, 29 Sep 2016 at 00:06 Paul Jack via swift-evolution <swift-evolution@swift.org> wrote:
So previously there were a few threads on the "strong self/weak self
dance" but they didn't seem to get anywhere. For instance:

https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160201/008713.html
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160215/010759.html
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160208/009972.html

...and possibly others.

I'd like to propose something even easier (and more specific) than all
of the above discussions. Specifically, I'd like to introduce a new
automagic closure variable, $self, whose presence in a closure would
cause that closure to weakly capture self in a safe manner.

As a concrete example, let's imagine a UIViewController for a login
form. Under this proposal, the following code:

func viewDidLoad() {
    self.loginForm.onSubmit = {
         let f = $self.loginForm
         $self.startLoginRequest(email:f.email.text, pwd:f.pwd.text)
    }
}

...would be treated by the compiler as equivalent to:

func viewDidLoad() {
    self.loginForm.onSubmit = {
         [weak self] in
         if let selfie = self {
             let f = selfie.loginForm
             selfie.startLoginRequest(email:f.email.text,
             pwd:f.pwd.text)
         }
    }
}

Note the "if let" there: If self no longer exists, the closure does not
execute at all, but if self does exist, then it exists for the entirety
of the execution of the closure (ie, self won't vanish as a side-effect
of some statement in the closure.) I think these semantics obey the
principle of least surprise; $self can be treated by the developer as a
strong reference.

However, that does mean that $self can only be used in a closure that's
(a) Void or (b) Optional. In the latter case, returning nil when self
doesn't exist seems like reasonable/expected behavior.

It would be a compile-time error to use both $self and normal self in
the same closure.

I'd like to keep this simple, meaning $self always does the above and
nothing else. So, if you need an unowned self, you still need the
original syntax; if your closure needs a non-Optional return type, you
still need the original syntax; etc.

Thoughts?

-Paul
_______________________________________________
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


(Jay) #7

I think Sean's idea for [required refName] on this is the better one in
terms of syntax and clarity of what's going on. It's fairly clear that the
required refs are weak but become strong during the closure execution, and
that since they're 'required' the closure goes away if they do.

In practice, with lazy zeroing I think it would not be viable to zero the
closure ref until calling it was attempted and the strong ref on its
required captures failed.
One for the 'deferred' pile I guess :stuck_out_tongue:

···

On Thu, 29 Sep 2016 at 01:27 Chris Lattner <clattner@apple.com> wrote:

On Sep 28, 2016, at 4:42 PM, Jay Abbott via swift-evolution < > swift-evolution@swift.org> wrote:

It could potentially be a breaking change if the default for @escaping
closures were made to be weak-capturing.

Ok, but source breaking changes need extreme justification. A primary
goal of Swift 3 was to provide source compatibility going forward.

-Chris

Since the weak-capturing pattern is only really desirable for @escaping
closures, and (I think) it would be the usual preference, could @escaping
also imply weak-capturing for all references (not just self)? Then there
would be another syntax for strong-capturing-escaping closures.
Non-escaping closures could a) strongly capture references; or b) existing
strong references stay strong and weak ones stay weak, meaning no
ref-counts need to change at all when passing them.

On Thu, 29 Sep 2016 at 00:06 Paul Jack via swift-evolution < > swift-evolution@swift.org> wrote:

So previously there were a few threads on the "strong self/weak self
dance" but they didn't seem to get anywhere. For instance:

https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160201/008713.html

https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160215/010759.html

https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160208/009972.html

...and possibly others.

I'd like to propose something even easier (and more specific) than all
of the above discussions. Specifically, I'd like to introduce a new
automagic closure variable, $self, whose presence in a closure would
cause that closure to weakly capture self in a safe manner.

As a concrete example, let's imagine a UIViewController for a login
form. Under this proposal, the following code:

func viewDidLoad() {
    self.loginForm.onSubmit = {
         let f = $self.loginForm
         $self.startLoginRequest(email:f.email.text, pwd:f.pwd.text)
    }
}

...would be treated by the compiler as equivalent to:

func viewDidLoad() {
    self.loginForm.onSubmit = {
         [weak self] in
         if let selfie = self {
             let f = selfie.loginForm
             selfie.startLoginRequest(email:f.email.text,
             pwd:f.pwd.text)
         }
    }
}

Note the "if let" there: If self no longer exists, the closure does not
execute at all, but if self does exist, then it exists for the entirety
of the execution of the closure (ie, self won't vanish as a side-effect
of some statement in the closure.) I think these semantics obey the
principle of least surprise; $self can be treated by the developer as a
strong reference.

However, that does mean that $self can only be used in a closure that's
(a) Void or (b) Optional. In the latter case, returning nil when self
doesn't exist seems like reasonable/expected behavior.

It would be a compile-time error to use both $self and normal self in
the same closure.

I'd like to keep this simple, meaning $self always does the above and
nothing else. So, if you need an unowned self, you still need the
original syntax; if your closure needs a non-Optional return type, you
still need the original syntax; etc.

Thoughts?

-Paul
_______________________________________________
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


(Jay) #8

Sean, yeah that's kind of what I was suggesting for @escaping closures - it
should be the default... but it is a breaking change and as Chris pointed
out that would need extreme justification... plus it would be a behaviour
change with no syntax change, so code that was never "upgraded" would be
very difficult to tell the original intention. I like the idea but I don't
know if I can come up with "extreme justification" for it.

···

On Thu, 29 Sep 2016 at 01:46 Sean Heber <sean@fifthace.com> wrote:

Now that I think about this, wouldn't that be a better default behavior?
All captures are this "required" type which means all closures are typed as
optional. To override that behavior, you'd have to explicitly declare a
weak or unowned capture instead and if you did that for all reference
captures, the closure's type would be non-optional as they are now. Seems
like that'd be safer. I'll shut up now.

l8r
Sean

Sent from my iPad

On Sep 28, 2016, at 7:25 PM, Sean Heber <sean@fifthace.com> wrote:

Pretty sure this is all way out of scope, but something about this made me
think about this idea (which maybe isn't unique or is maybe even
unworkable), but imagine something like where a new capture type is added
such as "required" (for lack of another name right now). And the way this
works is similar to unowned, but it makes the entire closure "weak" in such
a way that the moment any of the required captures go nil, any references
to that closure instance also effectively become nil.

So modifying the example:

func viewDidLoad() {
    self.loginForm.onSubmit = {[required self]
         let f = self.loginForm
         self.startLoginRequest(email:f.email.text, pwd:f.pwd.text)
    }
}

So in this case, "required self" means self is effectively "unowned" but
any references to this closure would have to be weak optional like: weak
var onSubmit: (()->Void)? So that when the view controller gets
deallocated, the closure goes with it and references become nil.

l8r
Sean

Sent from my iPad

On Sep 28, 2016, at 6:42 PM, Jay Abbott via swift-evolution < > swift-evolution@swift.org> wrote:

It could potentially be a breaking change if the default for @escaping
closures were made to be weak-capturing.

Since the weak-capturing pattern is only really desirable for @escaping
closures, and (I think) it would be the usual preference, could @escaping
also imply weak-capturing for all references (not just self)? Then there
would be another syntax for strong-capturing-escaping closures.
Non-escaping closures could a) strongly capture references; or b) existing
strong references stay strong and weak ones stay weak, meaning no
ref-counts need to change at all when passing them.

On Thu, 29 Sep 2016 at 00:06 Paul Jack via swift-evolution < > swift-evolution@swift.org> wrote:

So previously there were a few threads on the "strong self/weak self
dance" but they didn't seem to get anywhere. For instance:

https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160201/008713.html

https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160215/010759.html

https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160208/009972.html

...and possibly others.

I'd like to propose something even easier (and more specific) than all
of the above discussions. Specifically, I'd like to introduce a new
automagic closure variable, $self, whose presence in a closure would
cause that closure to weakly capture self in a safe manner.

As a concrete example, let's imagine a UIViewController for a login
form. Under this proposal, the following code:

func viewDidLoad() {
    self.loginForm.onSubmit = {
         let f = $self.loginForm
         $self.startLoginRequest(email:f.email.text, pwd:f.pwd.text)
    }
}

...would be treated by the compiler as equivalent to:

func viewDidLoad() {
    self.loginForm.onSubmit = {
         [weak self] in
         if let selfie = self {
             let f = selfie.loginForm
             selfie.startLoginRequest(email:f.email.text,
             pwd:f.pwd.text)
         }
    }
}

Note the "if let" there: If self no longer exists, the closure does not
execute at all, but if self does exist, then it exists for the entirety
of the execution of the closure (ie, self won't vanish as a side-effect
of some statement in the closure.) I think these semantics obey the
principle of least surprise; $self can be treated by the developer as a
strong reference.

However, that does mean that $self can only be used in a closure that's
(a) Void or (b) Optional. In the latter case, returning nil when self
doesn't exist seems like reasonable/expected behavior.

It would be a compile-time error to use both $self and normal self in
the same closure.

I'd like to keep this simple, meaning $self always does the above and
nothing else. So, if you need an unowned self, you still need the
original syntax; if your closure needs a non-Optional return type, you
still need the original syntax; etc.

Thoughts?

-Paul
_______________________________________________
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


(Charles Srstka) #9

How do reference cycles created by accidental implicit captures rank in the list of commonly encountered runtime issues? It’s gotta be high, isn’t it? Especially if you limit the scope to memory leaks—it seems every time I’ve had to track down one of those since ARC came out, it’s been either an unintended closure capture or something KVO-related.

Closures are, for me, the #1 time that I switch to super-defensive coding mode. Requiring explicit semantics to capture variables would surely reduce a lot of memory issues, and also fix crashes that could be caused by objects being deallocated in the wrong order. The quality of code in general would improve. Is that extreme? I don’t know, but I certainly wouldn’t complain if it happened.

Charles

···

On Sep 28, 2016, at 7:48 PM, Jay Abbott via swift-evolution <swift-evolution@swift.org> wrote:

Sean, yeah that's kind of what I was suggesting for @escaping closures - it should be the default... but it is a breaking change and as Chris pointed out that would need extreme justification... plus it would be a behaviour change with no syntax change, so code that was never "upgraded" would be very difficult to tell the original intention. I like the idea but I don't know if I can come up with "extreme justification" for it.


(Sean Heber) #10

Yeah, hard to say how extreme it needs to be to justify this sort of change. Does running into unintentional retain cycles when using closures for event handlers *constantly* count as extreme? :slight_smile:

In a typical garbage collected language, using closures like this is common and a non-issue, but in Swift the ownership in these situations is something you have to actively think about which is a distraction when you want to spend your time thinking about what you're building instead. It reminds me of manual memory management (which of course it is).

l8r
Sean

···

Sent from my iPad

On Sep 28, 2016, at 7:48 PM, Jay Abbott <jay@abbott.me.uk> wrote:

Sean, yeah that's kind of what I was suggesting for @escaping closures - it should be the default... but it is a breaking change and as Chris pointed out that would need extreme justification... plus it would be a behaviour change with no syntax change, so code that was never "upgraded" would be very difficult to tell the original intention. I like the idea but I don't know if I can come up with "extreme justification" for it.

On Thu, 29 Sep 2016 at 01:46 Sean Heber <sean@fifthace.com> wrote:
Now that I think about this, wouldn't that be a better default behavior? All captures are this "required" type which means all closures are typed as optional. To override that behavior, you'd have to explicitly declare a weak or unowned capture instead and if you did that for all reference captures, the closure's type would be non-optional as they are now. Seems like that'd be safer. I'll shut up now.

l8r
Sean

Sent from my iPad

On Sep 28, 2016, at 7:25 PM, Sean Heber <sean@fifthace.com> wrote:

Pretty sure this is all way out of scope, but something about this made me think about this idea (which maybe isn't unique or is maybe even unworkable), but imagine something like where a new capture type is added such as "required" (for lack of another name right now). And the way this works is similar to unowned, but it makes the entire closure "weak" in such a way that the moment any of the required captures go nil, any references to that closure instance also effectively become nil.

So modifying the example:

func viewDidLoad() {
    self.loginForm.onSubmit = {[required self]
         let f = self.loginForm
         self.startLoginRequest(email:f.email.text, pwd:f.pwd.text)
    }
}

So in this case, "required self" means self is effectively "unowned" but any references to this closure would have to be weak optional like: weak var onSubmit: (()->Void)? So that when the view controller gets deallocated, the closure goes with it and references become nil.

l8r
Sean

Sent from my iPad

On Sep 28, 2016, at 6:42 PM, Jay Abbott via swift-evolution <swift-evolution@swift.org> wrote:

It could potentially be a breaking change if the default for @escaping closures were made to be weak-capturing.

Since the weak-capturing pattern is only really desirable for @escaping closures, and (I think) it would be the usual preference, could @escaping also imply weak-capturing for all references (not just self)? Then there would be another syntax for strong-capturing-escaping closures. Non-escaping closures could a) strongly capture references; or b) existing strong references stay strong and weak ones stay weak, meaning no ref-counts need to change at all when passing them.

On Thu, 29 Sep 2016 at 00:06 Paul Jack via swift-evolution <swift-evolution@swift.org> wrote:
So previously there were a few threads on the "strong self/weak self
dance" but they didn't seem to get anywhere. For instance:

https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160201/008713.html
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160215/010759.html
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160208/009972.html

...and possibly others.

I'd like to propose something even easier (and more specific) than all
of the above discussions. Specifically, I'd like to introduce a new
automagic closure variable, $self, whose presence in a closure would
cause that closure to weakly capture self in a safe manner.

As a concrete example, let's imagine a UIViewController for a login
form. Under this proposal, the following code:

func viewDidLoad() {
    self.loginForm.onSubmit = {
         let f = $self.loginForm
         $self.startLoginRequest(email:f.email.text, pwd:f.pwd.text)
    }
}

...would be treated by the compiler as equivalent to:

func viewDidLoad() {
    self.loginForm.onSubmit = {
         [weak self] in
         if let selfie = self {
             let f = selfie.loginForm
             selfie.startLoginRequest(email:f.email.text,
             pwd:f.pwd.text)
         }
    }
}

Note the "if let" there: If self no longer exists, the closure does not
execute at all, but if self does exist, then it exists for the entirety
of the execution of the closure (ie, self won't vanish as a side-effect
of some statement in the closure.) I think these semantics obey the
principle of least surprise; $self can be treated by the developer as a
strong reference.

However, that does mean that $self can only be used in a closure that's
(a) Void or (b) Optional. In the latter case, returning nil when self
doesn't exist seems like reasonable/expected behavior.

It would be a compile-time error to use both $self and normal self in
the same closure.

I'd like to keep this simple, meaning $self always does the above and
nothing else. So, if you need an unowned self, you still need the
original syntax; if your closure needs a non-Optional return type, you
still need the original syntax; etc.

Thoughts?

-Paul
_______________________________________________
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