2

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?

H.song
  • 45
  • 4
  • 4
    Your code does not even *compile* because `foo1`, `foo2` are constants. Even if you declare them as variables you cannot assign `nil` to them. What did you really test? – Martin R Jul 14 '16 at 07:24
  • I never think CFGetRetainCount can show me about object retain count. try simple code: var foo1: NSNumber? = NSNumber(value: 0) print(CFGetRetainCount(foo1)). It's print very large number – larva Jul 14 '16 at 08:01
  • 3
    You are asking a question about code. It would be common courtesy to post the code that you are asking about, and not something you just made up on the spot. – gnasher729 Jul 14 '16 at 09:21
  • 1
    www.whentouseretaincount.com – bbum Jul 15 '16 at 15:23
  • 1
    @bbum That site is not working any more. – Sulthan Jul 15 '16 at 16:38
  • Loss of a gem. Here is a backup. http://sdarlington.github.io – bbum Jul 16 '16 at 02:20
  • **Never use `retainCount`**, see: [When to use -retainCount?](https://stackoverflow.com/questions/4636146/when-to-use-retaincount) – Top-Master Oct 20 '21 at 00:08

4 Answers4

4

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:

@implementation TestObject

- (instancetype)init {
    TestObject *result = [super init];

    NSLog(@"Retain count after creation: %@", @(self.retainCount));

    return result;
}

- (instancetype)retain {
    TestObject *result = [super retain];
    NSLog(@"Retain count after retain: %@", @(self.retainCount));

    return result;
}

- (oneway void)release {
    NSLog(@"Retain count before release: %@", @(self.retainCount));

    [super release];
}

- (void)dealloc {
    NSLog(@"Retain count before dealloc: %@", @(self.retainCount));
    [super dealloc];
}

@end

and used it in Swift instead of your NSObject:

var foo1: TestObject? = TestObject()
print("#")
print(CFGetRetainCount(foo1))
var foo2 = foo1
print("#")
print(CFGetRetainCount(foo1))
foo2 = nil
print("#")
print(CFGetRetainCount(foo1))
foo1 = nil

Resulting in:

Retain count after creation: 1
#
Retain count after retain: 2
2
Retain count before release: 2
Retain count after retain: 2
#
Retain count after retain: 3
3
Retain count before release: 3
Retain count before release: 2
#
Retain count after retain: 2
2
Retain count before release: 2
Retain count before release: 1
Retain count before dealloc: 1

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.

Sulthan
  • 128,090
  • 22
  • 218
  • 270
2

I study the obj-c/swift arc system.

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.

What's the reason of these phenomena?

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.

Community
  • 1
  • 1
Nikolai Ruhe
  • 81,520
  • 17
  • 180
  • 200
0

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 or CFGetRetainCount(): 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.

uliwitness
  • 8,532
  • 36
  • 58
0

I think this happens because when you pass your object to CFGetRetainCount function or any function the retain count should be increased by one.

ayman
  • 1