You can copy this playground verbatim:
var closures=[() -> Void]()
class Thing {
let name: String
init(_ name: String) { self.name=name }
}
class RefThingWrapper {
let thing: Thing
init(_ t: Thing) {
self.thing=t
closures.append { [thing, weak self] in // Even though `thing` captured strongly, `self` could still get deallocated.
print((self == nil) ? "`self` deallocated" : "`self` not deallocated")
print(thing.name) // Decided to use `thing` within closure to eliminate any chance of optimizations affecting the test results — which I found when I captured `self` strongly without using it.
}
}
}
struct ValThingWrapper {
let thing: Thing
init(_ t: Thing) {
self.thing=t
closures.append { [weak thing] in
print((thing == nil) ? "`thing` deallocated" : "`thing` not deallocated")
}
}
}
var wrapper=Optional(RefThingWrapper(Thing("Thing 1"))) // Test with reference type.
//var wrapper=Optional(ValThingWrapper(Thing("Thing 1"))) // Test with value type.
closures[0]()
wrapper=nil
closures[0]()
It demonstrates how a property of self
— whether self
is a reference or value type — can be captured within a closure independently of self
. Running the program as is demonstrates a captured property existing after self
has been deallocated. Testing with the value type wrapper demonstrates that, if weakly captured, the instance will be deallocated once the referencing value instance is deallocated.
I wasn't sure this was possible because when creating the closure at first, I forgot to initialize the property I was capturing. So the compiler complained — in the capture list — 'self' used before all stored properties are initialized
. So I figured self
was being captured implicitly, and only after digging deeper discovered otherwise.
Is this documented somewhere? I found this post by Joe Groff where he proposes:
For 'let' properties of classes, it'd be reasonable to propose having closures capture the property directly by default in this way instead of capturing 'self' (and possibly allowing referencing them without 'self.', since 'self' wouldn't be involved in any cycle formed this way).
This was back in 2015, and I didn't find any implemented proposals that arose from the discussion. Is there any authoritative source that communicates this behavior?
If you’re just asking for documentation on capture lists and reference types, see The Swift Programming Language Resolving Strong Reference Cycles for Closures. Also see the Language Reference: Capture Lists
If your capture list includes value type, you’re getting copy of the value.
var foo = 1
let closure = { [foo] in
print(foo)
}
foo = 42
closure() // 1; capturing value of `foo` as it was when the closure was declared
If your capture list includes a reference type, you’re getting a copy of the reference to that current instance.
class Bar {
var value: Int
init(value: Int) { self.value = value }
}
var bar = Bar(value: 1)
let closure = { [bar] in
print(bar.value)
}
bar.value = 2
bar = Bar(value: 3)
closure() // 2; capturing reference to first instance that was subsequently updated
These captured references are strong by default, but can optionally be marked as weak
or unowned
, as needed, too.
That Capture Lists document outlines the way that you can capture a property without capturing self
:
You can also bind an arbitrary expression to a named value in a capture list. The expression is evaluated when the closure is created, and the value is captured with the specified strength. For example:
// Weak capture of "self.parent" as "parent" myFunction { [weak parent = self.parent] in print(parent!.title) }
I’m not crazy about their code sample, but it illustrates the capture of a property without capturing self
, nonetheless.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With