There is no such thing as "the ARC of the object." What you're thinking about is the retain count. It is hard to imagine a number more meaningless than the retain count. It is either zero (in which case the object is gone, so you'll never see that), or it is "not zero."
The retain count is the number of ownership claims that have been put on an object. Any part of the system is free to make an ownership claim at any time. Any part of the system can remove their ownership claim at any time. There's a whole thing called the autorelease pool that holds ownership claims and will automatically release those claims "at some point in the future." It is completely normal for an object to have several autorelease retains on it at any given time. That will increase the retain count, but the retain count will drop later.
If retain counts were meaningless under MRC (and they were), they're completely bonkers under ARC, where the compiler is free to optimize them out anytime it can prove it doesn't matter, and often injects extra retains when it can't prove they're not needed (particularly related to function calls). So the actual value is even more meaningless. For example, under ARC, it is completely appropriate for test
to have an extra retain attached to it before calling CFGetRetainCount
just to make sure that test
isn't released too quickly.
If you have a memory management problem, you want to use tools like the memory graph debugger (and just looking for strong references and especially strong loops). Checking the retain count will only lie to you.
In your particular case, we can explore it a bit with swiftc -emit-sil
, starting at the point that we do string interpolation (i.e. the ""
in the last line):
// function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:)
%34 = function_ref @$SSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %35
%35 = apply %34(%30, %31, %32, %33) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %37
%36 = alloc_stack $String // users: %39, %37, %41
store %35 to %36 : $*String // id: %37
// function_ref specialized String.init<A>(stringInterpolationSegment:)
%38 = function_ref @$SSS26stringInterpolationSegmentSSx_tcs23CustomStringConvertibleRzs20TextOutputStreamableRzlufCSS_Tg5 : $@convention(method) (@owned String, @thin String.Type) -> @owned String // user: %40
%39 = load %36 : $*String // user: %40
%40 = apply %38(%39, %29) : $@convention(method) (@owned String, @thin String.Type) -> @owned String // user: %42
dealloc_stack %36 : $*String // id: %41
store %40 to %28 : $*String // id: %42
%43 = integer_literal $Builtin.Word, 1 // user: %44
%44 = index_addr %28 : $*String, %43 : $Builtin.Word // user: %58
%45 = metatype $@thin String.Type // user: %56
%46 = load %3 : $*LevelBuilder // users: %48, %47
=========
strong_retain %46 : $LevelBuilder // id: %47
%48 = init_existential_ref %46 : $LevelBuilder : $LevelBuilder, $AnyObject // user: %49
%49 = enum $Optional<AnyObject>, #Optional.some!enumelt.1, %48 : $AnyObject // users: %52, %51
// function_ref CFGetRetainCount
%50 = function_ref @CFGetRetainCount : $@convention(c) (Optional<AnyObject>) -> Int // user: %51
%51 = apply %50(%49) : $@convention(c) (Optional<AnyObject>) -> Int // user: %54
release_value %49 : $Optional<AnyObject> // id: %52
=========
I've marked the important part with ===
lines. A strong retain is put on test
. It's then wrapped up into a AnyObject?
wrapper to pass to the C function (GetRetainCount
). The function is called. And then the value of the Optional (i.e. test
) is released. So you should expect one extra retain when you call GetRetainCount
.
But if you re-compile this with -O
, you'll notice that there is no strong_retain
instruction. ARC sees that the extra retain isn't actually necessary and removes it. So that suggests that with optimization the retain count will be 1. I wonder if that's true:
$ swiftc main.swift
$ ./main
ARC: 2
$ swiftc -O main.swift
$ ./main
ARC: 1
Sure enough.