[swift-evolution] An upcoming proposal for simplifying leak free, safe closures.

Christopher Kornher ckornher at me.com
Tue Jun 28 11:44:23 CDT 2016


> On Jun 28, 2016, at 6:53 AM, admin at wheerd.de wrote:
> 
> Am 27.06.2016 20:55, schrieb Christopher Kornher:
>>> On Jun 27, 2016, at 2:45 AM, Manuel Krebber via swift-evolution <swift-evolution at swift.org> wrote:
>>> On 06/26/2016 09:10 PM, Christopher Kornher via swift-evolution wrote:
>>>> The core proposal:
>>>> ——————
>>>> Closures capturing object references should automatically capture all
>>>> object references as weak.
>>> In my code, most closures are used in a functional programming capacity,
>>> e.g. with map(), reduce, etc. Hence, most closures are non-escaping and
>>> local, where strong capture is the desired way. Otherwise I would have
>>> to litter everything with optional unwrapping or add the explicit
>>> capture definition which would both make the code less readable in my
>>> opinion.
>> I thought about this some more and it makes sense to treat
>> non-escaping closures are they are treated now. This might increase
>> the burden on the compiler especially because these closures may not
>> be declared inline. This would be far more straightforward than having
>> to worry about changes to object existence within one or more
>> invocations of a @nonescaping closure, especially in multi-threaded
>> code.
>> I do not think that this would be a significant change for developers
>> in practice. Any developer who would try to rely upon object
>> references changing within the application of a @nonescaping closure
>> would probably have read the manual very carefully :)
> 
> I am unsure whether treating closures differently depending on a @nonescaping attribute on a function is a good idea.
> The problem is that, theoretically, the closure might be stored in a variable or used with different functions. Then how do you decide whether the default is strong or weak capture?

The compiler/runtime (in theory at least) knows how a closure is going to be applied. I am not a compiler writer, but two versions of the closure could be created when necessary and the appropriate one could be used depending upon the context.

> 
> Example:
> 
> func f1(_ f : (Int) -> Int) {
> ...
> }
> 
> func f2(_ f : @noescape  (Int) -> Int) {
> ...
> }
> 
> let foo = Foo()
> let f : (Int) -> Int = {
>  return foo.x
> }
> 
> f1(f)
> f2(f)
> 
> In addition different default capture behaviour dependant on context could be confusing for developers I think.

The changes in behavior in the application of @noescaping closures are very much] edge case and are as likely to result from actions in other threads as anything else. So Capturing references as strong by default makes @noescaping closures more predictable and eliminates the potential for really some really subtle bug and race conditions.

For the same reason, implicit “let guards” in escaping closures should be applied early to improve predicability. Developers wanting to modify reference counts within closures are free to use traditional capture lists and capture and un-capture references as needed.

Memory management within closures should not be the default behavior, but it should be possible and discoverable. 


> 
>>>> 1) Closures with object references could be simplified further by
>>>> implicitly including ‘let’ guards for all object references:
>>> This sounds good for closures without return value, but how would you
>>> handle closures with non-optional non-void return values?
>> Good point.
>> 1) The simplest solution would be to have these closures require
>> capture lists to some or all object references. Given the prevalence
>> of the weak/strong dance, calling these out in some way probably
>> should be done anyway.
>> 2) Another option would be to eliminate the implicit nil check guards,
>> making all object references optional. The compiler would force these
>> to be unwrapped, so users would be guided to do the right thing.
>> 3) Just always weakly capturing all object references would treat all
>> closures uniformly.
> 
> I think I favour 2) out of these, even though it still means the code will potentially be littered with ?. or let guards...
> 
> Kind regards, Manuel
> 



More information about the swift-evolution mailing list