> On Jun 27, 2016, at 2:51 PM, Russ Bishop via swift-evolution > <[email protected]> wrote: > >> >> On Jun 27, 2016, at 12:35 AM, Christopher Kornher <[email protected] >> <mailto:[email protected]>> wrote: >> >> >>> On Jun 26, 2016, at 4:25 PM, Russ Bishop <[email protected] >>> <mailto:[email protected]>> wrote: >>> >>> >>>> On Jun 26, 2016, at 12:10 PM, Christopher Kornher via swift-evolution >>>> <[email protected] <mailto:[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. >>> >>> The problem is that strong reference capture is probably the far more >>> common case. >> >> Strong reference capture has not been more common in carefully written code >> in my experience. Swift is starting to be used for many different problem >> domains, so your experience may be different. Any examples of real-world >> code would be greatly appreciated. > > In my experience reference cycles are almost always caused by capturing self > strongly. > >> >> Sometimes closures contain the only references to objects, but this is not >> common in code that I have seen. It would be extremely rare for strong >> references to be required for all the references in closure with multiple >> references (the current default behavior). Long closures can reference many >> objects and it is all too easy to leave a reference out of the capture list >> and create a reference cycle. I believe that this pattern should be >> called-out (see below). >> >> Strong references are occasionally needed to ensure operations are performed >> when objects would otherwise be reclaimed, but I have not seen many of these >> cases. This pattern could be more common in other frameworks. I believe that >> this pattern should be called-out (see below). >> >> Multiple capture rules for closures can be supported if desired. The >> ```[required…``` capture qualifier in the original email is one way todo >> this. The question mark could be used in a way analogous to `try?` to >> identify closures using the proposed rules: >> >> ```let a:()->Void = {…}?``` >> or >> ``` let a:()->Void = ?{…}``` >> etc. >> >> This would obviously add more complexity but would still be an improvement, >> I believe. > > Possibly, though this inevitably ends up with the C++ lambda rules more or > less. To put it another way, why would Swift introduce a new way of handling > this instead of just requiring all closures to specify whether they want > strong default, weak default, or whatever and completely eliminate any > automatic behavior? Unfortunately that makes creating closures much more > annoying.
I took a quick look at C++ capture lists. Yeah, we don’t want those. But another default behavior for captures does make sense for Swift I think. More to come. >> >> >>> If you wanted to say that @noescape closures capture references strongly by >>> default, while escaping closures capture them weakly by default that may be >>> closer to reasonable for most situations but now you have magic behavior >>> with an even greater cognitive overhead. (I wonder if it would be more >>> common that @noescape captures strongly, escaping captures self weak by >>> default but other objects strongly… of course that would have even worse >>> cognitive overhead.) >> >> I did consider treating self differently, but this leads to some very >> strange cases when delegation, factories and other patterns are considered. >> >> This email was implicitly referring to escaping uses of closures. The same >> closure can be used as escaping or non-escaping. The Swift documentation >> states: >> >> "Marking a closure with @noescape lets the compiler make more aggressive >> optimizations because it knows more information about the closure’s >> lifespan." >> >> @noescape is essentially a hint to the compiler. Optimizers would be free to >> use strong or unowned references if they can determine that it is safe to do >> so without altering behavior. > > The majority of all closures are passed to non-escaping functional methods > like map, filter, etc. Not in the iOS programming that I do most of the time, but I am sure that the is often the case for other types of development. I rarely use object references in @noescape closures, but styles differ. > I don’t see how having them default to weak is a good thing in any capacity. After thinking about it, i agree. @noescape usages of closures should capture strongly. > Once we agree on that anything else but strong capture by default will by > necessity introduce a cognitive burden. When closures are used to define UI behaviors, capturing weak is a very reasonable default. When GUI elements go away, the reason for the behaviors usually goes with them. This is the context that inspired this. > > >> >>> >>> I don’t think there is a way to square this circle. Either you have one >>> “automagic” behavior that is wrong for some cases (whether strong or weak >>> is the default), or you require everyone to spam their closures with >>> explicit capture annotations even if there’s a default “capture everything >>> strongly” variant (see C++ lambdas). >> >> Yes, we are discussing tradeoffs. Writing correct code with closures will >> always require care.I believe that is is better to have safe, leak free code >> by default than not. I also believe that capturing strong references by >> default can probably lead to at least as many unexpected behaviors as >> capturing weak references. I don’t think that many developers would expect, >> for example, to see zombie view controllers that have not been associated >> with an active view hierarchy for weeks because a closure is holding on to a >> strong reference. > > I don’t agree that weak by default would equal “safe, leak free”. It might be > leak-free but it certainly isn’t necessarily safe. In the face of concurrency > you can’t overlook the “half executed” situation of consecutively doing > “self?.xyz(); self?.abc()”. Most programmers will assume both xyz() and abc() > execute or neither execute but that simply isn’t the case. By making weak the > default capture this existing problem is greatly exacerbated. > > > Russ_______________________________________________ > swift-evolution mailing list > [email protected] <mailto:[email protected]> > https://lists.swift.org/mailman/listinfo/swift-evolution > <https://lists.swift.org/mailman/listinfo/swift-evolution>
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
