[swift-evolution] Allowing `guard let self = self else { … }` for weakly captured self in a closure.

Evan Maloney emaloney at gilt.com
Wed Feb 3 23:57:02 CST 2016


I'm way-late to this discussion—I don't know how any of you get any coding done trying to keep up with this mailing list!—but anyway...

I propose:

let doSomething: () -> Void = { [guard self] in
    ...
}

[guard self] would effectively work like [weak self] in that it doesn't cause the closure itself to hold a strong reference to whatever is pointed to by self. But if self is still around when the closure is called, it upgrades it to a strong reference for the duration of the closure's execution.

So, when doSomething() is just sitting around not doing something, it doesn't prevent self from being deallocated.

If, by the time doSomething() is called, self is no longer there, doSomething() just returns without the closure executing. If self is there, then the closure executes with self as a strong reference inside. 

That way, self within the closure is already strong, so you can use it conveniently as a non-optional and without doing the strongSelf = self dance. You get the memory management benefit of a weak reference without the extra noise in your code needed to support it.

Ok, so what closures with a return value, you ask? How about something like:

let maybeDoSomething: () -> Bool = { [guard self else false] in
    ...
}

Here, maybeDoSomething() doesn't hold a strong reference to self. If it executes when self is still alive, the code within the braces executes with self as a strong reference. If self is gone, the value after the else is returned (the "return" itself is implied).

What do you think?


> On Jan 28, 2016, at 7:32 PM, Hoon H. via swift-evolution <swift-evolution at swift.org> wrote:
> 
> Thanks for your opinions.
> I am writing a formal proposal, and now I think it’d be fine to elide explicit `self` qualification after `guard let … `.
> 
> Also for your proposal, though I think mine is originated from different intention, but final conclusion overlaps with your intention, and I am still not sure what to do in this situation. Do you have some opinions?
> 
> — Hoon H.
> 
> 
> 
> 
> 
>> On 2016/01/06, at 10:46 AM, Jacob Bandes-Storch <jtbandes at gmail.com <mailto:jtbandes at gmail.com>> wrote:
>> 
>> +1.
>> 
>> Merely using "self?.something" repeatedly might produce unexpected behavior, if self becomes nil between calls. As I mentioned in another thread, in Obj-C, there is a warning for this (-Warc-repeated-use-of-weak).
>> 
>> In many cases, I use the pattern
>> 
>>     somethingAsync { [weak self] in
>>         guard let strongSelf = self else { return }
>> 
>>         // use strongSelf below
>>     }
>> 
>> But of course, this leads to the unnatural/unwieldy "strongSelf.property" all over the place.
>> 
>> I agree with Jordan that "guard let self = self" isn't the most satisfying syntax, but it has the advantage of being a very minimal grammar/syntax change, and its behavior is completely clear as long as the user is already familiar with guard.
>> 
>> We should also consider whether "self." is required after "guard let self = self". An explicit "guard let self = self" avoids the accidental-capture problem, so I think it's reasonable to allow unqualified property access for the remainder of the scope.
>> 
>> Jacob
>> 
>> On Tue, Jan 5, 2016 at 4:20 PM, Jordan Rose via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>> This has come up before, in a thread called "Proposal: weakStrong self in completion handler closures". I'm still not 100% happy with the syntax, but I like that "guard let" can handle non-Void non-Optional returns well, while 'weakStrong' cannot.
>> 
>> Jordan
>> 
>> 
>>> On Jan 5, 2016, at 16:02, Hoon H. via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>> 
>>> Currently, weakly captured `self` cannot be bound to `guard let …` with same name, and emits a compiler error.
>>> 
>>> 	class Foo {
>>> 		func test2(f: ()->()) {
>>> 			// … 
>>> 		}
>>> 		func test1() {
>>> 			test2 { [weak self] in
>>> 				guard let self = self else { return } // Error.
>>> 				print(self)
>>> 			}
>>> 		}
>>> 	}
>>> 
>>> Do we have any reason to disallow making `self` back to strong reference? It’d be nice if I can do it. Please consider this case.
>>> 
>>> 	class Foo {
>>> 		func getValue1() -> Int {
>>> 			return 1234
>>> 		}
>>> 		func test3(value: Int) {
>>> 			print(value)
>>> 		}
>>> 		func test2(f: ()->()) {
>>> 			// … 
>>> 		}
>>> 		func test1() {
>>> 			test2 { [weak self] in
>>> 				self?.test3(self?.getValue1()) // Doesn't work because it's not unwrapped.
>>> 
>>> 				self!.test3(self!.getValue1()) // Considered harmful due to `!`.
>>> 
>>> 				guard self != nil else { return }
>>> 				self!.test3(self!.getValue1()) // OK, but still looks and feels harmful.
>>> 
>>> 				guard let self1 = self else { return }
>>> 				self1.test3(self1.getValue1()) // OK, but feels ugly due to unnecessary new name `self1`.
>>> 
>>> 				guard let self = self else { return }
>>> 				self.test3(self.getValue1()) // OK.
>>> 
>>> 			}
>>> 		}
>>> 	}
>>> 
>>> This also can be applied to `if let` or same sort of constructs.
>>> 
>>> Even further, we can consider removing required reference to `self` after `guard let …` if appropriate.
>>> 
>>> 	guard let self = self else { return } 
>>> 	test3(getValue1()) // Referencing to `self` would not be required anymore. Seems arguable.
>>> 
>>> I think this is almost fine because users have to express their intention explicitly with `guard` statement. If someone erases the `guard` later, compiler will require explicit self again, and that will prevent mistakes. But still, I am not sure this removal would be perfectly fine.
>>> 
>>> I am not sure whether this is already supported or planned. But lacked at least in Swift 2.1.1.
>>> 
>>> — Hoon H.
>>> 
>>> 
>>> 
>>> 
>>> 
>>> 
>>> 
>>> 
>>> 
>>> 
>>> 
>>> 
>>> 
>>> 
>>> 
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
>> 
>> 
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
>> 
>> 
> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160204/2215093f/attachment.html>


More information about the swift-evolution mailing list