[swift-evolution] Proposal: Closures capture weak by default

Javier Soto javier.api at gmail.com
Tue Dec 8 09:38:34 CST 2015


I would like to also note that referencing an object (whether self or
otherwise) does not necessarily create a permanent retain cycle. Only
long-lived closures will cause these issues. In many other cases, retaining
objects inside a closure is actually what you want, to ensure they're still
alive by the time the closure is called.
In general, no matter what the default is, my hunch is that this is one of
those "hard problems" (essential complexity:
https://en.m.wikipedia.org/wiki/No_Silver_Bullet), and one must think about
the lifetime semantics of the constructions in their code and the APIs
they're using to understand the implications of using weak or strong
captures, and whether they will cause retain cycles or premature
deallocations.
On Tue, Dec 8, 2015 at 4:08 AM ilya via swift-evolution <
swift-evolution at swift.org> wrote:

> I don't think this would satisfy the principle of "working as expected",
> e.g.:
>
> class Computer {
>     func compute() -> Int { ... }
> }
>
> func test() {
>
>     let c = Computer()
>
>     // Expected: the closure executes in the background
>     dispatch_async(...) {
>
>         let computed = c.compute()
>         print(computed)
>
>         let computed2 = c.compute()
>         print(computed2)
>     }
>
> }
>
> Actual result if the proposal passes: the closure will not compile as c
> will be optional.
>
> Moreover, you'll have some beginners who will fix it with
>
>     dispatch_async(...) {
>
>         if let computed = c?.compute() {
>             print(computed)
>         }
>
>         if let computed2 = c?.compute() {
>             print(computed2)
>         }
>     }
>
> which has entirely different logic, as now any of those situations is
> possible:
> (1) both computed and computed2 are printed
> (2) none of those is printed
> (3) only computed is printed
>
> On Tue, Dec 8, 2015 at 14:15 Andrew Bennett via swift-evolution <
> swift-evolution at swift.org> wrote:
>
>> From https://swift.org/about/: "The most obvious way to write code
>> should also behave in a safe manner."
>>
>> To this end I think that closures should capture references types weakly
>> by default. Pretty much the only way I know of to (easily) create memory
>> issues with pure swift is to capture a strong reference in a closure.
>>
>> I think with consideration when designing asynchronous APIs this could be
>> quite painless.
>>
>> Cases weak may be excluded:
>>  * If the closure is @noescape
>>  * If the object's lifetime is provably limited to the block
>>  * If it's a value type
>>
>> I think the upsides by far outweigh the downsides.
>>
>> Upside:
>>  * no more surprises
>>  * safer code
>>
>> Downsides:
>>  * You may sometimes have to use optional chaining or similar to resolve
>> a weak reference.
>>  * Beginners need to understand optionals, but they're likely to do so
>> before learning blocks.
>>  * There's probably a few edge cases I haven't explored, and a few more
>> here:
>>
>> class Test {
>>    func doSomething(v: Int) { ... }
>>    func async(callback: Int->Void) {
>>       doWork { value in
>>            callback?(value)
>>       }
>>    }
>> }
>>
>> self.test = Test()
>> self.test.async(test.doSomething) // what is the lifetime
>> of test.doSomething?
>>
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
-- 
Javier Soto
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20151208/c51bf124/attachment.html>


More information about the swift-evolution mailing list