Easy strongified weak `self` in closures

weakify

(Kristian Trenskow) #1

Hi Swift Forum,

I'm completely new here, and I might be out on a limb when I'm asking this question. I just have a simple suggestion.

I find myself writing the code below again and again.

let myClosure = { [weak self] in
    guard let `self` = self else { return }
    // Actual closure code.
}

Could you, somehow, make this easier. This is some of the situations, where I actually miss C macros :grimacing:. I am therefore pitching the idea, that some kind of notation could be made in [weak self] that could tell the compiler to just bail out of the closure if some weak captured variable could not be unwrapped.


(Tino) #2

This might actually be one of the most annoying parts of Swift, and there have been some discussions to reduce the boilerplate — but all suggestions had their downsides as well.
Imho it might have been a good idea to make all captures weak by default, which would simplify small closures a lot (unless you need some parameters to be captured strong...).
It would also be possible to infer the type of capture (self? implies weak), but although this wouldn't break compatibility, it feels a little bit to magic...


(Charlie Monroe) #3

I'd personally suggest declaring that you only want the body of the closure to be executed when self is not nil with something like [autoweak self] or [required weak self].

This is both explicitely declarative about only wanting the code to be executed in context of non-nil self and a non-breaking addition.


(Kristian Trenskow) #4

You could make something like what @charlieMonroe suggests (like [autoweak self]) the default behaviour for escaping closures. That would eliminate a lot of unintentional retain cycles.


(Kristian Trenskow) #5

Making it default would be breaking behaviour, though.


(Tino) #6

Afair this has been discussed with [guard self] instead of autoweak ([Proposal] Guarded self in closures, Guarded closures).
It doesn't work with closures that return something, though: It might be possible to infer nil if the result is optional... but still, that's no general solution.
I think it's very hard to come up with a really elegant solution, and next to impossible to avoid breaking compatibility (unless it can live alongside with the old behavior) - and even if all those concerns are addressed, the idea still has to survive the evolution process.


#7

The problem here (IMO) is that this:

    guard let `self` = self else { return }

isn't really boilerplate, but is important documentation about what the closure does — since its behavior in the face of self == nil could reasonably be something more complicated, or sometimes could reasonably be handled later in the closure..

Given that, and that [guard self] is problematic if the closure returns a value, it makes more sense to keep the actual guard statement inside the closure, but look for ways to make it a little less wordy.

For example (and these are just suggestions to discuss, not a pitch), we can, and maybe already do — I can't remember if this was just talked about or actually done —, have this as official syntax:

    guard let self = self else { return }

Next, I've always thought that single unconditional control-transferring statements (return, break, continue, etc) should be syntactically equivalent to their singly { }-ified selves, so we could have this:

    guard let self = self else return

Finally, it might not be unreasonable to have a streamlined guard syntax, either for shadowing variables generally, or just for self specifically:

    guard self else return // or maybe 'guard let self else return'

To me, a syntax like that eliminates enough boilerplate to make me happy, but keeps useful explicitness about how the nil value is handled.


#8

First, for anyone who doesn't know, guard self = self else {} is now (finally) implemented to be valid.
I think this is sufficient for circumstances where you still want the function to run when self == nil, but in my experience that's mostly not the case when I use [weak self].

Been thinking about this some and got some ideas from reading the discussion for Optional iteration. I don't like autoweak and partially like [guard self], but I don't think it's necessary to add/adapt keywords when we already have syntax around optionals.

#1

let myClosure = { [weak self]? in
    //self is already unwrapped
}

This would probably the simplest syntactic change and incredibly easy to add for end users, while also remaining syntactically familiar.

#2
Another option would be to put it on the right side of in:

let myClosure = { in [weak self]? 
    //self is already unwrapped
}
//or since it's on the right side, in could no longer be required
let myClosure = { [weak self]? //self is already unwrapped }

This semantically puts it on the wrong side of the expression, but it would make more sense in cases where we also add a default return value:

let myClosure = { in [weak self]? else *defaultValue* // Or "[weak self] ?? *defaultValue*" ?
    //self is already unwrapped
}

#3
If in? was added as syntax, it could be a good candidate here:

let myClosure = { [weak self] in?
    //self is already unwrapped
}
// with default return value
let myClosure = { [weak self] in? else *defaultValue*
    //self is already unwrapped
}

To make it simple, these could compile to:

let myClosure = { [weak self] in
    guard let self = self else {
        return *defaultValueIfNecessary*
    }
}

#9

I'd also like put in a shout-out to [unowned self], which doesn't cause reference cycles but doesn't make self optional.

In some cases where you think you need [weak self] to avoid a reference cycle, you know that self isn't getting deallocated before the closure is deallocated. [unowned self] avoids the whole guard scenario.

The problem is that you actually have to think carefully whether unowned is going to work, because your app will crash if you're wrong (as opposed to just misbehaving if you get the other kinds of capture wrong).


(Kristian Trenskow) #10

I think there is a lot of interesting proposals here. Thank you for taking my first suggestion ever so serious. I, personally, like @GetSwifty's idea of [weak self] in? else *something*. Would that be able to be implemented without breaking compatibility? It would just be syntactic sugar?


(Matt Eaton) #11

The optional in? is an interesting idea. To make sure I understand this correctly, is this just unwrapping self for you using a guard and if the unwrap fails the closure is returned? Likewise, the else *defaultValue* values could be added and this would be the value returned from the failure to unwrap the self?


#12

This is the way I was thinking about it:

let myClosure = { [weak self] in? else *defaultValue* {

}

is equal to

let myClosure = { [weak self] in {
    guard let self = self else {
        return *defaultValue*
    } 
}

Having thought about it a bit more, there are pros and cons to a lot of the options to deal with this and I'm thinking there's probably a more generalized syntax that should be considered and applied for this and similar cases. (.e.g the aforementioned Optional Iteration).


(Jarod Long) #13

This is very close to the top of my personal list of quality of life improvements I'd like to see in Swift. I think the recurring nature of the topic is a good indicator that it's a common frustration.

I'd personally get a ton of value from a [guard self] that only works for void-returning closures and removes the self prefix requirement inside the closure. Something that works for non-void closures would be nice if a good solution can be found (the proposed in? seems interesting at first glance), but imo there's still enough value even if returning closures aren't supported.


#14

Throwing a random syntactical "?" at a problem involving optionals seems to be getting more popular lately, and it makes me unhappy because it seems so random.

Also, semantically, this particular problem isn't so much about self being optional, as about self being nil, so I'm not sure a "?" is appropriate.

IAC, there's a larger problem, in that capture lists can capture more than just self, and putting a ? on in prevents discriminating which captured optionals are to be guarded.


(Jon Shier) #15

? is an existing optional pattern matcher, so it's not random at all.


#16

I hadn't even thought about that aspect. I wonder if that could be supported currently. E.g. after guard self = self, self no longer has to be used explicitly.

It would be nice to get rid of one of the few places self is required.


(Jon Hull) #17

Agreed. I basically always use either [unowned self] or the basic strong self, depending on the use-case (i.e. whether I am storing the closure on an ancestor or not).

Another option we may not have considered is some sort of annotation on the storage site that tells it to check for a reference cycle and tells the block to convert to 'unowned self' if there is one.


(Kristian Trenskow) #18

I don't know the process here. If we wanted to turn this into a proposal, how does that work?


#19

As of Swift 4.2.1, you can already do this:

class Test {
    func foo() {
        let closure: () -> () = { [weak self] in
            if let self = self {
                // do someting
            }

            guard let self = self else {
                return
            }
        }
    }
}

unless I'm missing something.


(Kristian Trenskow) #20

This is the issue. We were discussing syntactic sugar for dealing with all that boilerplate.

So something like this was suggested.

let closure = () -> () = { [weak self] in?
    // self is unwrapped - or closure has returned.
}

Also if the closure returns a value it could be like this

let closure = () -> (Int) = { [weak self] in? else 0
    // self is unwrapped - or closure has returned with 0
}