I studying obj-c/swift arc system. print log on retain count of created instance by CFGetRetainCount func.
I expected reference count like these
let foo1 = NSObject() // foo1 retain count 1
let foo2 = foo1 // foo1 retain count 2
foo2 = nil // foo1 retains count 1
foo1 = nil // foo1 retain count 0. release
but actually..
let foo1 = NSObject() // foo1 retain count 2
let foo2 = foo1 // foo1 retain count 3
foo2 = nil // foo1 retain count 2
foo1 = nil // foo1 retain count 1. release
and print retain count of NSObject() directly..
print(CFGetRetainCount(NSObject())) // retain count 1
basically, NSObject() has 1 retain count. and release object when retain count reach 2.
I know increase retain count when link with instance strongly. but just created instance has 1 retain count and release instance when retain count became 1 not 0. what's the reason of these phenomena?
In short: It has to be at least 1, because when the retain count goes to 0 the object goes away (so there is no object to ask what its retain count is).
A longer answer might be this video I did for a friend ages ago: https://www.youtube.com/watch?v=cBN--I31Xjo
But really, if you're using ARC, you shouldn't look at the retain count. ARC will insert retains as needed, and it's perfectly fine for ARC to retain the object 5 times at this point, as long as it later releases it 5 times as well.
That's the problem with
-retainCount
orCFGetRetainCount()
: Since the whole point of retain counts is shared ownership, you should not care who else owns a reference, you should only care about how many references your code holds and that it properly gives them up by ensuring it has no circular strong references.I think this happens because when you pass your object to CFGetRetainCount function or any function the retain count should be increased by one.
You are heading the wrong way if you try to understand how ARC and retain count work on a high level. See here and here and here.
In the compiler ARC is a very complex system, underlying many layers of optimization. It is difficult to make sense of the actual retain count value. Also, it is affected by the optimization level.
If you really want to dive deeper here's a paper on ARC in Swift.
TL;DR The way you are logging can affect the results, you might get a temporary ownership when logging, therefore increasing all your printing results by 1.
The following answer is a simplification because you shouldn't really worry about the real value in
retainCount
.The retain count keeps a count how many references (owners) the given object has. When created, there is exactly one owner, therefore the retain count is set to 1. Every time the object gets a new owner (
retain
), the retain count is increased by one. Every time the object loses and owner (release
) the retain count is decreased by one.Note that the
retainCount
can never reach zero. If the number of owners is 1 and you lose the owner, the object is deallocated, the count is not decreased then.For a better test, I have created an Obj-C class, compiled without ARC:
and used it in Swift instead of your
NSObject
:Resulting in:
Which is basically what you would expect but there are additional retains and releases around every
CFGetRetainCount
when you get a temporary ownership when passing the object into the function.This is one of the examples why you should never read the value of
retainCount
. It has no debugging value, which is also mentioned in the documentation.