The ARC rules cited above state:
By default, local variables of automatic storage duration do not have precise lifetime semantics. Such objects are simply strong references which hold values of retainable object pointer type, and these values are still fully subject to the optimizations on values under local control.
The lifetimes in question are method arguments, which are local variables. So according to the ObjC ARC rules, which are more or less inherited by Swift, these variable lifetimes may be optimized.
Now, the Swift compiler is actually much more friendly, and only fully optimizes the lifetimes of copy-on-write "value types", Array
, Set
, Dictionary
, and String
. The rationale is that (1) such "value types" don't generally have well-defined deinitialization, regardless of the element type, and (2) it is critical for performance to optimize the lifetime of copy-on-write data structures.
Here's more background from an earlier post:
You notice this with initializers and setters because these methods take ownership their arguments by default, and are therefore responsible for destroying them (whenever the implementation of the method pleases).
The simple rule is to follow the formal specification fo ARC, and use withExtendedLifetime
to explicitly indicate the end of a values lifetime whenever you unsafely hold a pointer to its storage.
For example, if you expect x
to be destroy after a <side effect>
that does not reference x
, then do this:
{
var x = ...
withExtendedLifetime(x) {
use(x)
<side effect>
}
}
The somewhat more complicated Swift compiler rules are:
You can skip withExtendedLifetime
if x
is not a predefined "value type" and the "side effect" above is any of the following:
- a use of a weak or unowned reference to an object kept alive by
x
- a use of an unsafe pointer into an object kept alive by
x
- an "externally visible" side effect, such as I/O, closing a file handle, or thread synchronization. (This only applies if deinitialization of
x
also has an externally visible side effect.)
These more complicated rules exist to handle patterns that appear with some regularity in Swift code.
Rather than breaking existing code by improving lifetime optimizations in these cases, we will allow programers to opt-into lifetime optimization in Swift when using explicit ownership modifiers like borrowing
, and consuming
. These modifiers are supported on parameter types, but not yet on other local variables, and the implementation is a work-in-progress.