Effective Objective-C 2.0: Item 36: Avoid Using retainCount (Deprecated)

Item 36: Avoid Using retainCount

Objective-C uses reference counting for memory management (seeItem 29). Each object has a counter that determines how many other things are interested in keeping it alive. When an object is created, the retain count is greater than zero. Retaining and releasing manipulate the count by incrementing and decrementing it, respectively. When the count reaches zero, the object is deallocated and destroyed.

A method defined by the NSObject protocol allows you to obtain the current retain count for an object:

- (NSUInteger)retainCount

However, ARC has deprecated this method. In fact, the compiler will throw an error if you try to call it when using ARC, just as it does with such methods as retainrelease, and autorelease. Even though this method is officially deprecated, it is often misunderstood, and its use should be avoided. If you are not using ARC, which you really should be, it’s still possible to use this method without getting a compiler error. So it is still important to understand why this method is to be avoided.

It may seem as though this method does a reasonable and useful job. It returns the retain count, after all, which is clearly an important piece of information about every object. The problem is, though, that the absolute retain count is often completely irrelevant and not what you need to know. Even if you use this method only for debug purposes, it’s usually not at all helpful.

The first important reason this method is not useful is that its value is the retain count at a given timeBecause this value does not include any decrements that are going to happen during a subsequent autorelease pool drain (see Item 34), the value is not necessarily a true representation of the count. Therefore, the following code is very bad:

while ([object retainCount]) {
    [object release];
}

The first reason this code is wrong is that it will keep dropping the retain count until the object is deallocated, without regard for any autoreleases that may be pending. If the object was also in an autorelease pool at the time, when that pool is drained, the object is further released, and a crash will certainly occur.

Second, the code is dangerous because retainCount will never return 0; an optimization within the release behavior of objects means that an object is deallocated when it is released if its retain count is 1. Otherwise, the decrement happens. Therefore, the retain count should never officially reach 0. Unfortunately, this code does sometimes work, largely through sheer luck rather than judgment. Modern runtimes usually simply crash when the while loop iterates after the object was eventually deallocated.

Such code should never be necessary. Memory management should be done before this sort of code would ever need to be implemented. There should never be unbalanced retains leaving objects with a positive retain count when you thought they should be deallocated. In such a scenario, the problem should be diagnosed by working out what is still retaining the object and why it hasn’t released it.

You may also try to use the retain count and wonder why the value sometimes looks very large. For example, consider the following code:

NSString *string = @"Some string";
NSLog(@"string retainCount = %lu", [string retainCount]);

NSNumber *numberI = @1;
NSLog(@"numberI retainCount = %lu", [number retainCount]);

NSNumber *numberF = @3.141f;
NSLog(@"numberF retainCount = %lu", [numberFloat retainCount]);

The output of that code on Mac OS X 10.8.2, 64-bit compiled withClang 4.1, is this:

string retainCount = 18446744073709551615
numberI retainCount = 9223372036854775807
numberF retainCount = 1

The first number is 264 – 1 and the second number is 263 – 1. The retain counts of these objects are both very large because they are representing singleton objects. NSString is implemented as a singleton object, if possible. It is possible if the string is a compile-time constant, as in the example. In this case, the compiler makes a special object, placing the data for the NSString object within the application binary, and uses that instead of creating an NSStringobject at runtime. NSNumber does a similar thing, using a concept known as tagged pointers for certain types of values. In this scheme, there is no NSNumber object; rather, the pointer value itself holds all the information about the number. The runtime then detects that a tagged pointer is being used during message dispatch (see Item 11) and manipulates the pointer value accordingly to behave as though there were a full NSNumber object. This optimization is done only for certain cases, though, which is why the floating-point number in the example has a retain count of 1, as it does not use this optimization.

In addition, the retain counts of singletons such as these never change. Retains and releases are no-ops. The fact that the retain count can behave like this and even that the two singletons return different values for the retain count should indicate again that it is not always a good value to consider. If you were relying on NSNumberobjects to have incrementing and decrementing retain counts and then tagged pointers were implemented post facto, your code would be wrong.

But what if you want to use the retain count for debugging purposes? Even then, it is usually not helpful. The retain count might not be as accurate as you think, owing to the object’s being in an autorelease pool. Also, other libraries can interfere with the retain count by retaining and/or releasing the object. If you inspect the count, youmay incorrectly assume that it has changed because of your code rather than from deep within another library. Take, for example, the following code:

id object = [self createObject];
[opaqueObject doSomethingWithObject:object];
NSLog(@"retainCount = %lu", [object retainCount]);

What is the retain count? It could be anything. The call todoSomethingWithObject: may have added the object to multiple collections, retaining it in the process. Or the call may have retained the object multiple times and autoreleased it multiple times, some of which are still pending an autorelease pool drain. The retain count is therefore unlikely to be useful to you.

When can you use retainCount? The best answer: never, especially given that Apple has officially deprecated it when using ARC.

Things to Remember

Image The retain count of an object might seem useful but usually is not, because the absolute retain count at any given time does not give a complete picture of an object’s lifetime.

Image When ARC came along, the retainCount method was deprecated, and using it causes a compiler error to be emitted.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值