Why Obj-C instance have 1 retain count Just create

2020-04-02 04:36发布

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?

4条回答
冷血范
2楼-- · 2020-04-02 05:14

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.

查看更多
够拽才男人
3楼-- · 2020-04-02 05:23

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

查看更多
The star\"
4楼-- · 2020-04-02 05:25

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.

查看更多
贼婆χ
5楼-- · 2020-04-02 05:27

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.

查看更多
登录 后发表回答