《编写高质量iOS与OS X代码的52个有效方法》--第五章 第36条
(ps:此乃读书笔记,加深记忆,仅供大家参考)
第36条 不要使用retainCount
NSObject协议中定义了下列方法,用于查询对象当前的保留计数:
- (NSUInteger)retainCount
然而ARC已经将此方法废弃了。若在不启用ARC的环境下编程,那么扔可调用此方法,所以还是必须讲清楚为何不应使用此方法。
这个方法看上去似乎挺合理的、挺有用的。但问题在于,保留计数的绝对值一般都与开发者所应留意的事情完全无关。即便是在调试时才调用此方法,通常也还是无所助益的。
此方法之所以无用,其首要原因在于:它所返回的保留计数只是某个给定时间点上的值。该方法并未考虑到系统会稍后把自动释放池清空,因而不会将后续的释放操作从返回值里减去,这样的话,此值就未必能真实反映实际的保留计数了。
有下面这段代码:
NSString *stirng = @"Some string";
NSLog(@"string retainCount = %lu", [stirng retainCount]);
NSNumber *numberI = @1;
NSLog(@"numberI retainCount = %lu", [numberI retainCount]);
NSNumber *numberF = @3.14f;
NSLog(@"numberF retainCount = %lu", [numberF retainCount]);
在64位Mac OS X 10.8.2系统中,用Clang4.1编译后,这段代码输出的消息如下:
string retainCount = 18446744073709551615
numberI retainCount = 9223372036854775807
numberF retainCount = 1
第一个对象的保留计数是2^64 - 1,第二个对象的保留计数是2^63 - 1.由于二者皆为“单例对象”(singleton object),所以其保留计数都很大。系统会尽可能把NSStirng实现成单例对象。如果字符串像本例所举的这样,是个编译常量(compile-time constant),那么就可以这样来实现了。在这用情况下,编译器会把NSString对象所表示的数据放到应用程序的二进制文件里,这样的话,运行程序时就可以直接用了,无须再创建NSString对象。NSNumber也类似,它使用了一种叫做“标签指针”(tagged pointer)的概念来标注特定类型的数值。这种做法不使用NSNumber对象,而是把与数值有关的全部消息都放在指针值里面。运行期系统会在消息派发(参见第11条)期间检测到这种标签指针,并对它执行相应操作,使其行为看上去和真正的NSNumber对象一样。这种优化只在某些场合使用,比如范例中的浮点数对象就没有优化,所以其保留计数就是1。
另外,像刚才所说的那种单例对象,其保留计数绝对不会变。这种对象的保留及释放操作都是“空操作”(no-op)。
那么,只为了调试而使用retainCount方法行不行呢?即便只为调试,此方法也不是很有用。以下列代码为例:
id object = [self createObject];
[opaqueObject doSomethingWithObject:object];
NSLog(@"retainCount = %lu", [object retainCount]);
object的保留计数是多少呢?这个计数可以是任意值。“doSomethingWithObject:”方法也许会将对象加到多个collectin中,而这些collection均会保留此对象。这个方法还可能会多次保留并自动释放此对象,而其中某些自动释放操作要留待系统稍后清空自动释放池时才执行。因此,保留计数的实际值就不是那么有用了。
要点
- 对象的保留计数看似有用,实则不然,因为任何给定时间点上的“绝对保留计数”(absolute retain count)都无法反映对象生命期的全貌。
- 引入ARC之后,retainCount方法就正式废止了,在ARC下调用该方法会导致编译器报错。