iOS中的retainCount

时间:2024-12-17 08:33:56

我们都知道iOS中采用引用计数的技术来管理内存,当一个对象没有任何一个地方引用的时候会自动释放,此时的retainCount为0,而且提供了一个-(NSInteger)retainCount的方法来获得对象当前的持有数。

这个概念清楚而明确,不过,让我们看两段代码:

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

这段代码的运行结果是什么?

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

这一段呢?

在Xcode5.0, ARC关闭的情况下运行,结果是:

2014-01-13 22:08:13.217 blockTest[2732:303] retainCount = 9223372036854775807

2014-01-13 22:08:13.218 blockTest[2732:303] retainCount = 1

这个很出人意料啊,第二段看上去很符合我们的预期,但第一段就有点让人掉眼镜了。

查阅了一下官方的文档,第一句就是“Do not use this method.”,后面给出了说明,因为Autorelease pool的存在,对于内存的管理会相当复杂,retainCount就不能用作调试内存时的依据了。这样对于第一段的结果就可以理解了,可能系统对于这一个特殊的对象有特殊的处理(没准framework里面有早就创建了这个对象了),于是我们拿到了一个非常出人意料的结果。

想想见过的如下的代码就很有点吓人了:

    while ([a retainCount] > 0) {
[a release];
}

如果运行结果正确,那么这是多么幸运的一个人啊!

但对于内存的思考我们可以更进一步,看一段代码:

#import <Foundation/Foundation.h>

@interface Person : NSObject

@property(nonatomic, retain) NSString *name;
@property(nonatomic, assign) NSInteger age; @end @implementation Person
- (id)init
{
self = [super init];
if (self) {
self.name = @"name";
self.age = 20;
}
return self;
} - (void)dealloc
{
NSLog(@"dealloc");
[super dealloc];
} @end int main(int argc, const char * argv[])
{
Person *per1 = [[Person alloc] init];
//NSLog(@"retainCount = %lu -- 0",[per1 retainCount]);
[per1 release];
per1.age = 22;
NSLog(@"retainCount = %lu -- 1, age = %ld",[per1 retainCount], per1.age);
//[per1 retain];
//per1.age = 23;
//NSLog(@"retainCount = %lu -- 2, age = %ld",[per1 retainCount], per1.age); return 0;
}

这段代码的运行结果居然是:

2014-01-13 22:17:11.619 blockTest[2758:303] dealloc

2014-01-13 22:17:11.621 blockTest[2758:303] retainCount = 1 -- 1, age = 22

这个问题位于http://www.cocoachina.com/ask/questions/show/106777#36483,个人猜测是对象确实销毁了,毕竟dealloc都调用了,但内存里面数据仍然存在,立刻打印是存在内容的,至于retainCount不是0,只能说明对象即使已经销毁了,这个值仍然可能大于0。