[swift-evolution] Proposal: Allow `[strong self]` capture in closures and remove the `self` requirement therein

Greg Parker gparker at apple.com
Tue Dec 15 19:15:19 CST 2015


> On Dec 15, 2015, at 5:02 PM, Drew Crawford <drew at sealedabstract.com> wrote:
> 
>> 
>> Can you explain what is so evil about func evil() when it is called from an asynchronously-executed closure? I don't see an obvious bug here.
> 
> Apologies for earlier brevity, perhaps it would be helpful for me to present a more complete, realistic example.  Paste the following into main.swift:
> 
> import Foundation
> 
> print("Hello, World!")
> 
> typealias evilArg = [String:String]
> var strongReference: evilArg! = nil
> func evil(foo:evilArg ) {
>     strongReference = foo
> }
> 
> final class Photo {
>     var data = [UInt8](count: 100000000, repeatedValue: 0) //a large amount of data
>     let metadata: [String: String] = [:] //a small amount of data
>     func save() {
>         dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)) {
>             evil(self.metadata)
>         }
>     }
> }
> let p = Photo()
> p.save()
> //leaks Photo, data, and metadata
> 
> let sema = dispatch_semaphore_create(0)
> dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER)
> 
> In this example, the memory usage at the end of the program is 100MB:
> 
> <Screen Shot 2015-12-15 at 6.26.52 PM.png>
> 
> This is because Photo, data, and metadata are all leaked by the evil function.
> 
> This is the (surprising?) behavior specified in the Swift Book:
> 
>> [A capture occurs when] the closure’s body accesses a property of the instance, such as self.someProperty, or because the closure calls a method on the instance, such as self.someMethod(). In either case, these accesses cause the closure to “capture” self, creating a strong reference cycle.
> 
> 
> 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 *actually* captures Photo (and therefore data)
> 
> The capture of Photo (data) is somewhat clear when we write
> 
> evil(self.metadata)
> 
> But it is hidden when we write
> 
> evil(metadata)
> 
> As you propose.
> 
> I think that if we are going to have semantics that capture Photo (data), it had better look like it in a cursory inspection.  The existing syntax is not as great as it could be, but it provides a clue.

I think I see. Your contention is that `self.metadata` is only just barely a sufficient clue to the capture behavior, and that `[strong self]` at the top of the closure would no longer be good enough.

We could make it more difficult for unaware programmers to *write* the `[strong self]` version, at least. When a programmer writes just `metadata` in a closure, the compiler could continue to suggest using `self.metadata` and not suggest using `[strong self]`.


-- 
Greg Parker     gparker at apple.com     Runtime Wrangler


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20151215/ab794925/attachment.html>


More information about the swift-evolution mailing list