I like it. Accidental capture of a variable is probably one of the primary 
causes of retain cycles in modern Swift code. Requiring the capture to be 
explicit would take a lot of the surprise out of it and force the developer to 
think about the capture semantics.

+1.

Charles

> On Jun 26, 2016, at 2:10 PM, Christopher Kornher via swift-evolution 
> <[email protected]> wrote:
> 
> I may be too late for Swift 3, but I am planning to propose changes to the 
> default behavior for closures capturing object references. The introduction 
> of Swift Playgrounds has raised the importance of simplifying the coding of 
> leak-free, crash-free closures. New developers should not have to understand 
> closure memory management to start writing useful and correct code.
> 
> The topic of the closure weak/strong dance has been discussed on this list 
> before. This proposal differs from previous proposals in that it will 
> eliminate the dance altogether by default. I am very interested in hearing 
> others’ opinions as to whether the benefits outweigh the costs of various 
> options.
> 
> I have found that Swift’s capture lists and rules are a bit of a mystery to 
> many experienced developers, even though Swift’s closure capture rules are 
> very similar to those of Objective-C. Capture lists are probably thought of 
> as opaque incantations by many new Swift developers if they are aware of them 
> at all. Capture lists should, ideally, not be needed for sensible and safe 
> default behavior.
> 
> This discussion is iOS / OS X centric and uses terms from those domains, but 
> these issues should be applicable to almost any codebase that uses closures 
> capturing object references.
> 
> Capture lists are required by the fact that object references are captured as 
> `strong` by default, often leading to strong reference cycles and memory 
> leaks.
> 
> Use of ‘unowned’ 
> ————————
> Many examples of using closures capture self as `unowned`. Often, this 
> pattern does not scale well beyond simple examples. iOS and MacOS 
> applications with dynamic UIs, for example, switch between numerous views and 
> view controllers. These objects are dereferenced and reclaimed when they are 
> no longer needed. Closures capturing these objects as `unowned` crash when 
> the references are accessed.
> 
> Unfortunately, ‘unowned’ captures are tempting because they eliminate `guard` 
> and `if let` constructs and avoid littering code with optional unwrapping. 
> They are also slightly more performant, but this difference is probably 
> negligible in most application code.
> 
> Capturing escaping object references as `unowned` is only safe when object 
> lifetimes are perfectly understood. In complex systems, it is difficult to 
> predict execution order. Even if object lifetimes are perfectly understood 
> when code is originally written, seemingly innocuous changes to complex 
> systems can negate original assumptions.
> 
> For these reasons, I believe that capturing object references as `unowned` 
> should be considered an advanced optimization technique.
> 
> I now routinely create closures that capture `self` and other object 
> references as ‘weak’ even if I think that I feel that `unowned ` would be 
> safe. This may not be the absolutely most performant solution, but it is 
> straightforward and robust.
> 
> 
> The core proposal:
> ——————
> 
> Closures capturing object references should automatically capture all object 
> references as weak.
> 
> The closure defined in:
> 
> ```
> class ClosureOwner2 {
>     var aClosure: (() -> Void)?
>     
>     func aMethod() {
>         aClosure = { [weak self] in
>            self?.someOtherMethod()
>        }
>     }
> 
>     func someOtherMethod(){}
> }
> ```
> 
> would normally be written as: 
> 
> ```
>         aClosure = {
>            self?.someOtherMethod()
>         }
> ```
> Closures that can be optimized to safely capture object references as 
> `unowned` can use the current syntax.
> 
> Swift 2 closure without explicit capture lists for object references will not 
> compile.
> 
> Capturing strong object references can be very useful in certain 
> circumstances and have a straightforward syntax:
> 
> ```
>         aClosure = { [strong self] in
>            self.someOtherMethod()
>         }
> ```
> 
> 
> Alternatives / Modifications / Improvements(?):
> —————————————————————
> 
> 1) Closures with object references could be simplified further by implicitly 
> including ‘let’ guards for all object references: 
>  
>         aClosure = {
>            // This is included implicitly at the top of the closure:
>       // guard let strongSelf = self else { return }
> 
>             /*strongSelf*/ self.someOtherMethod()
>       print( “This will not appear when self is nil.” )
> 
>       … other uses of strongSelf…
> 
>         }
> 
> This would have the effect of making the execution of the closure dependent 
> upon the successful unwrapping of all of its object references. Object 
> references that are not required to unwrap successfully can be captured as 
> `weak’ if desired. 
> 
> 
> 2) Some of the magic in #1 could be eliminated by introducing a new capture 
> type:  ‘required’ to specify ‘weak guarded’ captures, allowing the example 
> closure to be written:
> 
> ```
>         aClosure = { [required self] in
>            self.someOtherMethod()
>       print( “This will not appear when self is nil.” )
>       print( “This is not the default behavior and the reason for this is 
> clearly stated.” ) 
>         }
> ```
> This reduces the amount of code required, but may increase the cognitive 
> burden over using the current syntax.
> 
> `required` is called `guard` in the “Simplified notation” proposal: 
> https://gist.github.com/emaloney/d34ac9b134ece7c60440 
> <https://gist.github.com/emaloney/d34ac9b134ece7c60440>
> 
> This proposal will differ from the “Simplified notation” proposal in that all 
> object references would be captured as `weak` by default in all closures.
> 
> Summary
> —————-
> Closures with objects references occur frequently in Swift code and the use 
> of closures is probably only going to increase. Current capture list rules 
> are not developer friendly and force developers to deal with subtle 
> asynchronous memory management issues. This increases the cognitive burden 
> particularly for new developers.
> 
> Closures should be safe and straightforward by default.
> 
> Enhancement #1 without #2 would probably be the easiest option for new 
> developers and result the smallest code size. The question is: is it too 
> “magical” and are the changes in execution behavior going to be significant 
> in practice?
> 
> The rules for closure object references with enhancement #1 can be stated 
> succinctly:
> 
> By default:
> 
> Closures do not affect reference counts of the objects that they reference.
> All object references used within a closure must unwrap successfully for the 
> closure to execute.
> 
> I believe that these are safe, sensible and understandable rules that will 
> eliminate the need for capture lists for many closures. What do you think?
> 
> 
> _______________________________________________
> swift-evolution mailing list
> [email protected]
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to