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
