Proposal: weakStrong self in completion handler closures


(Robert Vojta) #1

Hi all,

let’s say we have a completion handler closure for some function (networking, …) and we have [weak self] there. Example …

doSomething() { [weak self] result in
  …
}

… then we can use self?.whatever to access self properties, methods. Or we can try to check if self exists ...

guard let strongSelf = self else { return }

… and use strongSelf.

Can we introduce [weakStrong self] with following behavior:

- self is a weak reference
- when the closure is going to be executed, all weakStrong weak references are checked if they do exist
- if they do exist, they’re strong referenced for the closure and the closure is executed
- if they don’t exist, closure is not executed

doSomething() { [weakStrong self] result in
  // Closure code is not executed if self no longer exists
  // self is a strong reference now
}

What do you think? Does it make sense?

My motivation is to get rid off of the repetitive code like this one:

doSomething() { [weak self] result in
  guard let strongSelf = self else { return }
  strongSelf.doSomethingWithResult(result)
}

Robert


The Future Of [weak self] Rebinding
(Jacob Bandes-Storch) #2

Another approach would be to simply allow

guard let self = self else { return }

···

On Sat, Dec 5, 2015 at 3:48 AM Robert Vojta <rvojta@me.com> wrote:

Hi all,

let’s say we have a completion handler closure for some function
(networking, …) and we have [weak self] there. Example …

doSomething() { [weak self] result in
  …
}

… then we can use self?.whatever to access self properties, methods. Or we
can try to check if self exists ...

guard let strongSelf = self else { return }

… and use strongSelf.

Can we introduce [weakStrong self] with following behavior:

- self is a weak reference
- when the closure is going to be executed, all weakStrong weak
references are checked if they do exist
- if they do exist, they’re strong referenced for the closure and the
closure is executed
- if they don’t exist, closure is not executed

doSomething() { [weakStrong self] result in
  // Closure code is not executed if self no longer exists
  // self is a strong reference now
}

What do you think? Does it make sense?

My motivation is to get rid off of the repetitive code like this one:

doSomething() { [weak self] result in
  guard let strongSelf = self else { return }
  strongSelf.doSomethingWithResult(result)
}

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


(Kevin Ballard) #3

How would weakStrong work if the closure has a non-Void return type?

Personally, I just use

guard let this = self else { return }

in this scenario. It's not much trouble to type `this.foo` instead of
`self.foo` (it's even the same number of letters).

-Kevin Ballrd

···

On Sat, Dec 5, 2015, at 03:48 AM, Robert Vojta wrote:

Hi all,

let’s say we have a completion handler closure for some function
(networking, …) and we have [weak self] there. Example …

doSomething() { [weak self] result in
  …
}

… then we can use self?.whatever to access self properties, methods. Or
we can try to check if self exists ...

guard let strongSelf = self else { return }

… and use strongSelf.

Can we introduce [weakStrong self] with following behavior:

- self is a weak reference
- when the closure is going to be executed, all weakStrong weak
references are checked if they do exist
- if they do exist, they’re strong referenced for the closure and the
closure is executed
- if they don’t exist, closure is not executed

doSomething() { [weakStrong self] result in
  // Closure code is not executed if self no longer exists
  // self is a strong reference now
}

What do you think? Does it make sense?

My motivation is to get rid off of the repetitive code like this one:

doSomething() { [weak self] result in
  guard let strongSelf = self else { return }
  strongSelf.doSomethingWithResult(result)
}

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


(Javier Soto) #4

My understanding was that [weak variable] behaved that way in Swift already
(but now I'm not certain and can't find a definitive answer on the book),
creating a strong reference. Otherwise this turns into inconsistent
behavior, where the variable could deallocate and become nil at any point
inside the closure.

Those semantics are also important in a case with a closure within a
closure:

function1(){ [weak self] in
function2() {
// the value (If present) inside Optional<Self> is a strong reference here
}
}

···

On Sat, Dec 5, 2015 at 11:22 AM Jacob Bandes-Storch via swift-evolution < swift-evolution@swift.org> wrote:

Another approach would be to simply allow

guard let self = self else { return }
On Sat, Dec 5, 2015 at 3:48 AM Robert Vojta <rvojta@me.com> wrote:

Hi all,

let’s say we have a completion handler closure for some function
(networking, …) and we have [weak self] there. Example …

doSomething() { [weak self] result in
  …
}

… then we can use self?.whatever to access self properties, methods. Or
we can try to check if self exists ...

guard let strongSelf = self else { return }

… and use strongSelf.

Can we introduce [weakStrong self] with following behavior:

- self is a weak reference
- when the closure is going to be executed, all weakStrong weak
references are checked if they do exist
- if they do exist, they’re strong referenced for the closure and the
closure is executed
- if they don’t exist, closure is not executed

doSomething() { [weakStrong self] result in
  // Closure code is not executed if self no longer exists
  // self is a strong reference now
}

What do you think? Does it make sense?

My motivation is to get rid off of the repetitive code like this one:

doSomething() { [weak self] result in
  guard let strongSelf = self else { return }
  strongSelf.doSomethingWithResult(result)
}

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

--
Javier Soto


(Davide De Franceschi) #5

I agree that "weak-strong" is a commonly used (and correct) pattern, and I also wish that there were an easier way to do that. I also did file a radar myself about that.

But I don't have any idea of the correct way of implementing it because the actually desired behaviour in the "failing" case can change depending on your use-case.

Most of the times you just want to `guard let strongSelf = self else { return }`, but that works when you return `Void` and you don't want to manage this "failure" in any other way...
Should `weak-strong` be allowed only for closures that `-> Void`?
What if you want to fire a "completionBlock" anyway?

While the need to remove this common boilerplate feels strong, I'm not certain this would be the right way. Can't really suggest a better alternative though.

···

On 5 Dec 2015, at 19:21, Jacob Bandes-Storch via swift-evolution <swift-evolution@swift.org> wrote:

Another approach would be to simply allow

guard let self = self else { return }
On Sat, Dec 5, 2015 at 3:48 AM Robert Vojta <rvojta@me.com <mailto:rvojta@me.com>> wrote:
Hi all,

let’s say we have a completion handler closure for some function (networking, …) and we have [weak self] there. Example …

doSomething() { [weak self] result in
  …
}

… then we can use self?.whatever to access self properties, methods. Or we can try to check if self exists ...

guard let strongSelf = self else { return }

… and use strongSelf.

Can we introduce [weakStrong self] with following behavior:

- self is a weak reference
- when the closure is going to be executed, all weakStrong weak references are checked if they do exist
- if they do exist, they’re strong referenced for the closure and the closure is executed
- if they don’t exist, closure is not executed

doSomething() { [weakStrong self] result in
  // Closure code is not executed if self no longer exists
  // self is a strong reference now
}

What do you think? Does it make sense?

My motivation is to get rid off of the repetitive code like this one:

doSomething() { [weak self] result in
  guard let strongSelf = self else { return }
  strongSelf.doSomethingWithResult(result)
}

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


(Robert Vojta) #6

How would weakStrong work if the closure has a non-Void return type?

Allowed only for optional (closure) parameters?

doSmth(handler: (() -> Bool)?) { [weakStrong self] in
}

weakStrong makes sense for async only where self (or anything else) can disappear. It does not make sense for @noescape, ...

Personally, I just use

guard let this = self else { return }

in this scenario. It's not much trouble to type `this.foo` instead of
`self.foo` (it's even the same number of letters).

Way to do. But if you decide to move code outside the closure you're forced to replace this. with self. or delete this. The code shouldn't differ (this vs self vs strongSelf) if it's in the closure or outside of it.

Robert

···

On 06 Dec 2015, at 09:19, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:


(Jacob Bandes-Storch) #7

When you use "weak self", self becomes Optional inside the closure, and
indeed it *can* become nil partway through. This is the same as __weak in
Objective-C, and there's actually a clang warning for it (search for
"repeated-use-of-weak" or see
http://devstreaming.apple.com/videos/wwdc/2013/404xbx2xvp1eaaqonr8zokm/404/404.pdf
).

That's why "guard let strongSelf = self" is important, and I think "guard
let self = self" would add value without adding confusion.

The current behavior is easily demonstrated:

import Foundation

class C {
    func doStuff() {
        dispatch_async(dispatch_get_global_queue(0, 0)) { [weak self] in
            print("self is \(self), waiting...")
            usleep(500_000)
            print("self is \(self), waiting...")
            usleep(500_000)
            print("self is \(self)")
        }
    }
}

var obj: C? = C()
obj!.doStuff()

usleep(750_000)
print("main thread - releasing")
obj = nil

sleep(2)

···

On Sat, Dec 5, 2015 at 1:24 PM, Javier Soto <javier.api@gmail.com> wrote:

My understanding was that [weak variable] behaved that way in Swift
already (but now I'm not certain and can't find a definitive answer on the
book), creating a strong reference. Otherwise this turns into inconsistent
behavior, where the variable could deallocate and become nil at any point
inside the closure.

Those semantics are also important in a case with a closure within a
closure:

function1(){ [weak self] in
function2() {
// the value (If present) inside Optional<Self> is a strong reference here
}
}
On Sat, Dec 5, 2015 at 11:22 AM Jacob Bandes-Storch via swift-evolution < > swift-evolution@swift.org> wrote:

Another approach would be to simply allow

guard let self = self else { return }
On Sat, Dec 5, 2015 at 3:48 AM Robert Vojta <rvojta@me.com> wrote:

Hi all,

let’s say we have a completion handler closure for some function
(networking, …) and we have [weak self] there. Example …

doSomething() { [weak self] result in
  …
}

… then we can use self?.whatever to access self properties, methods. Or
we can try to check if self exists ...

guard let strongSelf = self else { return }

… and use strongSelf.

Can we introduce [weakStrong self] with following behavior:

- self is a weak reference
- when the closure is going to be executed, all weakStrong weak
references are checked if they do exist
- if they do exist, they’re strong referenced for the closure and the
closure is executed
- if they don’t exist, closure is not executed

doSomething() { [weakStrong self] result in
  // Closure code is not executed if self no longer exists
  // self is a strong reference now
}

What do you think? Does it make sense?

My motivation is to get rid off of the repetitive code like this one:

doSomething() { [weak self] result in
  guard let strongSelf = self else { return }
  strongSelf.doSomethingWithResult(result)
}

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

--
Javier Soto


(Robert Vojta) #8

When you use "weak self", self becomes Optional inside the closure, and indeed it *can* become nil partway through. This is the same as __weak in Objective-C, and there's actually a clang warning for it (search for "repeated-use-of-weak" or see http://devstreaming.apple.com/videos/wwdc/2013/404xbx2xvp1eaaqonr8zokm/404/404.pdf).

Yep, that's correct.

That's why "guard let strongSelf = self" is important, and I think "guard let self = self" would add value without adding confusion.

Agree that weakStrong can be confusing and there's maybe better way to do it. But at least, "guard let self = self" would be enough and we can avoid ugly and long strongSel with it.

Robert

···

On 05 Dec 2015, at 22:36, Jacob Bandes-Storch <jtbandes@gmail.com> wrote:


(Robert Vojta) #9

Most of the times you just want to `guard let strongSelf = self else { return }`, but that works when you return `Void` and you don't want to manage this "failure" in any other way...
Should `weak-strong` be allowed only for closures that `-> Void`?

Can be and result will be optional. But it complicates things and makes it unclear. Which is not good.

What if you want to fire a "completionBlock" anyway?

Then don't use weakStrong and use weak strong dance.

While the need to remove this common boilerplate feels strong, I'm not certain this would be the right way. Can't really suggest a better alternative though.

Agree with you. I just wanted to start discussion about it, but not convinced about weakStrong as well.

Probably what Jacob proposed would be enough. Just allow "guard let self = self ..." at least to remove strongSelf, this, ...

Robert

···

On 6. 12. 2015, at 11:01, Davide De Franceschi via swift-evolution <swift-evolution@swift.org> wrote: