[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