<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div style="font-family: HelveticaNeue;" class=""><blockquote type="cite" class="">Can you explain what is so evil about func evil() when it is called from an asynchronously-executed closure?</blockquote></div><div style="font-family: HelveticaNeue;" class=""><br class=""></div><div style="font-family: HelveticaNeue;" class="">Apologies for earlier brevity, perhaps it would be helpful for me to present a more complete, realistic example. &nbsp;Paste the following into main.swift:</div><div style="font-family: HelveticaNeue;" class=""><br class=""></div><blockquote class="" style="font-family: HelveticaNeue; margin: 0px 0px 0px 40px; border: none; padding: 0px;"><div class="">import&nbsp;Foundation</div><div class=""><br class=""></div><div class="">print("Hello, World!")</div><div class=""><br class=""></div><div class="">typealias&nbsp;evilArg = [String:String]</div><div class="">var&nbsp;strongReference:&nbsp;evilArg! =&nbsp;nil</div><div class="">func&nbsp;evil(foo:evilArg&nbsp;) {</div><div class="">&nbsp; &nbsp;&nbsp;strongReference&nbsp;= foo</div><div class="">}</div><div class=""><br class=""></div><div class="">final&nbsp;class&nbsp;Photo {</div><div class="">&nbsp; &nbsp;&nbsp;var&nbsp;data = [UInt8](count: 100000000, repeatedValue: 0)&nbsp;//a large amount of data</div><div class="">&nbsp; &nbsp;&nbsp;let&nbsp;metadata: [String:&nbsp;String] = [:]&nbsp;//a small amount of data</div><div class="">&nbsp; &nbsp;&nbsp;func&nbsp;save() {</div><div class="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)) {</div><div class="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;evil(self.metadata)</div><div class="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;}</div><div class="">&nbsp; &nbsp;&nbsp;}</div><div class="">}</div><div class="">let&nbsp;p =&nbsp;Photo()</div><div class="">p.save()</div><div class="">//leaks Photo, data, and metadata</div><div class=""><br class=""></div><div class="">let&nbsp;sema =&nbsp;dispatch_semaphore_create(0)</div><div class="">dispatch_semaphore_wait(sema,&nbsp;DISPATCH_TIME_FOREVER)</div></blockquote><div style="font-family: HelveticaNeue;" class=""><br class=""></div><div class="" style="font-family: HelveticaNeue;">In this example, the memory usage at the end of the program is 100MB:</div><div class="" style="font-family: HelveticaNeue;"><br class=""></div><div class="" style="font-family: HelveticaNeue;"><img apple-inline="yes" id="197FDD80-DC75-4FC4-AB25-606B425EDA63" height="210" width="258" apple-width="yes" apple-height="yes" src="cid:F82AC3D2-D8A5-4366-8545-4EEE3ADA7BD9@austin.rr.com" class=""></div><div class="" style="font-family: HelveticaNeue;"><br class=""></div><div class="" style="font-family: HelveticaNeue;">This is because Photo, data, and metadata are all leaked by the evil function.</div><div class="" style="font-family: HelveticaNeue;"><br class=""></div><div class="" style="font-family: HelveticaNeue;">This is the (surprising?) behavior specified in the Swift Book:</div><div class="" style="font-family: HelveticaNeue;"><br class=""></div><div class="" style="font-family: HelveticaNeue;"><blockquote type="cite" class="">[A capture occurs when] the closure’s body accesses a property of the instance, such as self.someProperty, or&nbsp;because the closure calls a method on the instance, such as self.someMethod(). In either case, these accesses cause the&nbsp;closure to “capture” self, creating a strong reference cycle.<br class=""></blockquote></div><div class="" style="font-family: HelveticaNeue;"><br class=""></div><div class="" style="font-family: HelveticaNeue;">Even though evil may seem (to the casual programmer who does not read language specifications for funsies) like the closure captures only the evil argument `metadata`, it *<b class="">actually</b>* captures&nbsp;<b class="">Photo (and therefore data)</b></div><div class="" style="font-family: HelveticaNeue;"><br class=""></div><div class="" style="font-family: HelveticaNeue;">The capture of Photo (data) is somewhat clear when we write</div><div class="" style="font-family: HelveticaNeue;"><br class=""></div><blockquote class="" style="font-family: HelveticaNeue; margin: 0px 0px 0px 40px; border: none; padding: 0px;"><div class="">evil(self.metadata)</div><div class=""><br class=""></div></blockquote><span style="font-family: HelveticaNeue;" class="">But it is hidden when we write</span><div class="" style="font-family: HelveticaNeue;"><br class=""></div><blockquote class="" style="font-family: HelveticaNeue; margin: 0px 0px 0px 40px; border: none; padding: 0px;"><div class="">evil(metadata)</div></blockquote><br class="" style="font-family: HelveticaNeue;"><div class="" style="font-family: HelveticaNeue;">As you propose.</div><div class="" style="font-family: HelveticaNeue;"><br class=""></div><div class="" style="font-family: HelveticaNeue;">I think that if we are going to have semantics that capture Photo (data), it had better look like it in a cursory inspection. &nbsp;The existing syntax is not as great as it could be, but it provides a clue. &nbsp;I think even that the existing syntax isn't good enough, because I expect that many people are unaware of this particular dark corner. &nbsp;As you say:</div><div class="" style="font-family: HelveticaNeue;"><br class=""></div><div class="" style="font-family: HelveticaNeue;"><blockquote type="cite" class="">Can you explain what is so evil about func evil() when it is called from an asynchronously-executed closure? &nbsp;I don't see an obvious bug here.</blockquote><br class=""></div><div class="" style="font-family: HelveticaNeue;">It may very well be that there is no *obvious* bug, and so what we may actually need is a proposal to make the programmer even more explicit when referencing self, not less.</div><div class="" style="font-family: HelveticaNeue;"><br class=""></div><div class="" style="font-family: HelveticaNeue;">But in any case. &nbsp;For `strong self` to make sense, we would need to do one of two things:</div><div class="" style="font-family: HelveticaNeue;"><br class=""></div><div class="" style="font-family: HelveticaNeue;">1. &nbsp;We could change Swift semantics to only capture metadata in this example, however this is a breaking change, and a very very nontrivial one. &nbsp;I do not know why it is specified this way in the first place, perhaps a designer can weigh in on that. &nbsp;I do know that ObjC is very similar, so there may be compatibility implications.</div><div class="" style="font-family: HelveticaNeue;"><br class=""></div><div class="" style="font-family: HelveticaNeue;">2. &nbsp;We could introduce additional syntax to provide compiler diagnostics to guard in this case, e.g.</div><div class="" style="font-family: HelveticaNeue;"><br class=""></div><blockquote class="" style="font-family: HelveticaNeue; margin: 0px 0px 0px 40px; border: none; padding: 0px;"><div class="">func lessEvil(@noescape&nbsp;foo:evilArg&nbsp;) { //@noescape currently not supported for non-closure parameters</div><div class="">&nbsp; &nbsp; &nbsp;self.strongReference = foo //should generate diagnostic about escaping a @noescape parameter</div><div class="">}</div><div class=""><br class=""></div></blockquote><span style="font-family: HelveticaNeue;" class="">and then</span><br class="" style="font-family: HelveticaNeue;"><blockquote class="" style="font-family: HelveticaNeue; margin: 0px 0px 0px 40px; border: none; padding: 0px;"><div class=""><br class=""></div><div class=""><div class="">dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)) { [strong self] in</div><div class="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;evil(metadata) //should generate diagnostic that self is required for functions without @noescape</div><div class="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; lessEvil(metadata) //no diagnostic since parameter is @noescape</div><div class="">}</div></div></blockquote><div class="" style="font-family: HelveticaNeue;"><br class=""></div><div class="" style="font-family: HelveticaNeue;">I think both of these fixes to the proposal are fairly impractical, but they would weaken my vote to -0.5.</div></body></html>