<html><head><meta http-equiv="Content-Type" content="text/html charset=us-ascii"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class="">Good idea, Kevin. Something like this seems reasonable (though I admit I just skimmed the message). In the mean time, maybe we can add a warning for implicit captures with a different strength?</div><div class=""><br class=""></div><div class="">(I'd also be happy to treat "unowned" + "weak" as "error, be explicit" rather than "strong".)</div><div class=""><br class=""></div><div class="">There is one case where an explicit capture behaves differently from an implicit one: capturing local variables (as opposed to local constants). There's an example of this in the Swift Programming Language book, in the reference section: "<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/doc/uid/TP40014097-CH32-ID544" class="">Capture Lists</a>".</div><div class=""><br class=""></div><div class="">Jordan</div><br class=""><div><blockquote type="cite" class=""><div class="">On Dec 8, 2015, at 12:44, Kevin Ballard via swift-evolution <<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>> wrote:</div><br class="Apple-interchange-newline"><div class="">
<title class=""></title>
<div class=""><div class="">In Swift code today, when using nested closures, if the inner closure weakly captures an object (e.g. `self`) that isn't otherwise captured by the outer closure, the outer closure implicitly strongly captures the object. This behavior is unlikely to be what the programmer intended, and results in unwanted object lifetime extension without making it obvious in the code.<br class=""></div>
<div class=""> </div>
<div class="">In practice, you'll find this happening in code that looks like<br class=""></div>
<div class=""> </div>
<div class=""><span class="font" style="font-family:menlo, consolas, 'courier new', monospace, sans-serif">class SomeViewController: UIViewController {</span><br class=""></div>
<div class=""><span class="font" style="font-family:menlo, consolas, 'courier new', monospace, sans-serif"> // ...</span><br class=""></div>
<div class=""><span class="font" style="font-family:menlo, consolas, 'courier new', monospace, sans-serif"> func foo(url: NSURL) {</span><br class=""></div>
<div class=""><span class="font" style="font-family:menlo, consolas, 'courier new', monospace, sans-serif"> let task = NSURLSession.sharedSession().dataTaskWithURL(url) { data, response, error in</span><br class=""></div>
<div class=""><span class="font" style="font-family:menlo, consolas, 'courier new', monospace, sans-serif"> let result = // process data</span><br class=""></div>
<div class=""><span class="font" style="font-family:menlo, consolas, 'courier new', monospace, sans-serif"> dispatch_async(dispatch_get_main_queue()) { [weak self] in</span><br class=""></div>
<div class=""><span class="font" style="font-family:menlo, consolas, 'courier new', monospace, sans-serif"> self?.handleResult(result)</span><br class=""></div>
<div class=""><span class="font" style="font-family:menlo, consolas, 'courier new', monospace, sans-serif"> }</span><br class=""></div>
<div class=""><span class="font" style="font-family:menlo, consolas, 'courier new', monospace, sans-serif"> }</span><br class=""></div>
<div class=""><span class="font" style="font-family:menlo, consolas, 'courier new', monospace, sans-serif"> task.resume()</span><br class=""></div>
<div class=""><span class="font" style="font-family:menlo, consolas, 'courier new', monospace, sans-serif"> }</span><br class=""></div>
<div class=""><span class="font" style="font-family:menlo, consolas, 'courier new', monospace, sans-serif">}</span><br class=""></div>
<div class=""> </div>
<div class="">In here, at first glance it looks like the view controller is being weakly referenced. But in actuality, the view controller is being retained for the duration of the background processing, and is then only converted to a weak reference at the moment where it tries to hop back on to the main thread. So it's basically the worst of both worlds; the view controller lives far longer than intended, but it goes away right at the moment where it could be useful again. It's even worse if the programmer expected the view controller's deinit() to cancel the network task, as that can never happen. The fix for this code is to move the `[weak self]` up to the outer closure, but since the outer closure never actually touches self directly, it's not immediately obvious that this is required.<br class=""></div>
<div class=""> </div>
<div class="">My proposal is to change the rules so that whenever a closure captures an object only because a nested closure did so, then the outer closure should capture it using the same ownership semantics (this includes unowned(unsafe), unowned(safe), weak, and strong). If there are multiple nested closures that capture it, then we use the following rules:<br class=""></div>
<div class=""> </div>
<div class="">* If all nested captures use the same ownership, then the outer capture uses that ownership.<br class=""></div>
<div class="">* If any nested capture is strong, the outer capture is strong.<br class=""></div>
<div class="">* If at least one nested capture is weak, and at least one capture is unowned or unowned(unsafe), the outer capture is strong. This is because there's no (safe) way to convert from weak -> unowned, or from unowned -> weak, and we should not crash upon the creation of the nested closure, so the outer capture must be strong.<br class=""></div>
<div class="">* If at least one nested capture is unowned(unsafe), and at least one nested capture is unowned(safe), then the outer capture is unowned(safe).<br class=""></div>
<div class=""> </div>
<div class="">This can be visualized with the following diagram, where the outer closures uses the right-most node that covers all the children:<br class=""></div>
<div class=""> </div>
<div class=""><span class="font" style="font-family: menlo, consolas, "courier new", monospace, sans-serif;"> .--- weak <-------------------------------.</span><span class="font" style="font-family: menlo, consolas, "courier new", monospace, sans-serif;"><br class=""></span></div>
<div class=""><span class="font" style="font-family: menlo, consolas, "courier new", monospace, sans-serif;"> / \</span><span class="font" style="font-family: menlo, consolas, "courier new", monospace, sans-serif;"><br class=""></span></div>
<div class=""><span class="font" style="font-family: menlo, consolas, "courier new", monospace, sans-serif;">strong < + no capture</span><span class="font" style="font-family: menlo, consolas, "courier new", monospace, sans-serif;"><br class=""></span></div>
<div class=""><span class="font" style="font-family: menlo, consolas, "courier new", monospace, sans-serif;"> \ /</span><span class="font" style="font-family: menlo, consolas, "courier new", monospace, sans-serif;"><br class=""></span></div>
<div class=""><span class="font" style="font-family: menlo, consolas, "courier new", monospace, sans-serif;"> '--- unowned(safe) <--- unowned(unsafe) --'</span><span class="font" style="font-family: menlo, consolas, "courier new", monospace, sans-serif;"><br class=""></span></div>
<div class=""> </div>
<div class="">-Kevin Ballard</div>
<img src="https://u2002410.ct.sendgrid.net/wf/open?upn=f6VunEbxOyO0-2FacKqVTeu6pGEEellWH1YmuP-2Bt5lLhP1j1zAal-2B1ZSFDmQZZd8jqSZ84Wk6aom-2F9vzaNXz6axiRCYUBJ1XCPnoQaa-2BRdpTjl9DjK73Td3SO2LjZsEh6KXUy40RgTH-2BHRogcIPyDU9cj2v9qu-2F1mszMFcr35eQ6R4Kx6jgBAAEHnw-2BBVfIjjPFHc6RDMdyFqztDOsykN-2BSWOgN6HMYyhg7AQ84UzGqfY-3D" alt="" width="1" height="1" border="0" style="height:1px !important;width:1px !important;border-width:0 !important;margin-top:0 !important;margin-bottom:0 !important;margin-right:0 !important;margin-left:0 !important;padding-top:0 !important;padding-bottom:0 !important;padding-right:0 !important;padding-left:0 !important;" class="">
</div>
_______________________________________________<br class="">swift-evolution mailing list<br class=""><a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a><br class="">https://lists.swift.org/mailman/listinfo/swift-evolution<br class=""></div></blockquote></div><br class=""></body></html>