在Objective C中为NSObject存储的保留计数在哪里

时间:2012-04-11 16:01:09

标签: objective-c memory-management

我很好奇内部保留/释放的工作方式。在表面上,似乎有一个与NSObject的每个实例相关的整数,当你分别调用-retain-release时,它会增加和减少。

但是看看NSObject,它所拥有的唯一实例变量是isa变量,用于确定其类类型。

那么存储单个对象的保留计数在哪里?并不是说我会用它来捣乱,而只是为了我自己的启发。

它是否与NSObject一起存储,但隐藏在某些Objective C实现细节中?如果是这样,那对我来说似乎是一个糟糕的设计。一个应该能够创建自己的根类并以类似的方式处理它们自己的保留/释放计数(不是一个好主意 - 一个人必须有非常有充分理由不使用NSObject)。

5 个答案:

答案 0 :(得分:15)

保留计数的存储位置取决于正在使用的运行时和类实现。

对于Apple的Objective-C运行时,您可以通过挖掘the source code of the Objective-C runtime来了解很多。

例如,如果您正在使用ARC(我认为即使您不使用),大多数对象的引用计数也会存储在哈希表中。请查看_objc_rootRetain中的runtime/objc-arr.mm函数。我不知道为什么他们这样做。也许这是一种将保留计数保持在一起以获得更好的缓存行为的方法(这在ARC下很重要,因为ARC比非ARC代码通常更频繁地调整保留计数)。

但是,有些类会覆盖retain及相关方法,并将保留计数存储在其他位置。例如,在调试内存泄漏时,我发现CALayer执行此操作。 CALayer将其保留计数存储在私有C ++实现对象中,而不是使用运行时的正常保留计数机制。这是相当令人沮丧的,因为这意味着仪器分配工具不记录CALayer对象的保留和释放。

答案 1 :(得分:7)

我们并不确切知道数据的存储方式,但我们可以排除一些选项:

私人执行变量

我们可以排除这一点,因为当我们遍历NSObject类的iVars时,我们只看到一个:isa,如此程序所示:

id object = [NSObject new];
Class meta = object->isa;

printf("class name: %s\n", class_getName(meta));

unsigned count;
Ivar *ivars = class_copyIvarList(meta, &count);

for (int i = 0; i < count; i++) {
    printf("iVar: %s\n", ivar_getName(ivars[i]));
}

free(ivars);

请注意,即使私有实现属性也存在于metdata类中。

私有属性

我们也可以对此进行排除,因为甚至私有属性都在类元数据中公开,如下例所示,NSObject类没有属性:

id object = [NSObject new];    
Class meta = object->isa;

printf("class name: %s\n", class_getName(meta));

objc_property_t *properties = class_copyPropertyList(meta, &count);

for (int i = 0; i < count; i++) {
    printf("property: %s\n", property_getName(properties[i]));
}

相关对象

这个很难排除,因为没有直接的方法来获取所有相关对象的列表。但是,由于关联对象的概念是非常新的,并且引用计数已经永远存在,我说这不太可能。

CoreFoundation struct-mangling

这是我最好的猜测。当您创建NSObject时,它是幕后的结构。这就是说实际的NSObject数据表示是这样的:

typedef struct CFObject {
    int retainCount;
    id isa;
} *CFObjectRef;

然后,当创建一个对象时:

id object_createInstance(...)
{
    CFObjectRef object = malloc(sizeof(struct CFObject));

    ...

    return (id) (object + sizeof(object->retainCount));
}

int object_retainCount(id self)
{
    CFObjectRef asObject = (CFObjectRef) (self - sizeof(asObject->retainCount));
    return asObject->retainCount;
}

我无法验证这一点,因为还有很多其他方法可以完成(例如,对象的整数映射)。

答案 2 :(得分:2)

这听起来不像,但为了以防万一...如果您考虑直接使用保留计数,don't

至于实现细节,WWDC 2011上的会话提到在ARC下,大多数引用计数实现已进入ObjC运行时。 Source for that is available,所以你可以自己找出它是如何工作的。对于手动引用计数,大部分ObjC行为都在CoreFoundation和libdispatch中复制,它们也是开源的 - 如果您希望自己实现类似的方案,那些可能具有教育意义。

一般来说,这是一个实现细节,出于同样的原因,许多事情都是:封装是一个很好的策略,特别是对于框架提供者。您不希望框架的用户具体取决于实现细节,因为这样您就无法在不破坏其代码的情况下更改您的实现。

答案 3 :(得分:0)

不知道这是否相关,但我偶然发现了Objective-C中有关高阶消息实现的blog post。作者将HOM对象实现为根类(即不从NSObject继承),实现如下:

@interface HigherOrderMessage {
    Class isa;
    NSUInteger retainCount;

    //not relevant to this question part
}

然后保留计数管理方法实现如下:

- (id)retain {
    __sync_add_and_fetch(&retainCount, 1);
    return self;
}

- (id)autorelease {
    [NSAutoreleasePool addObject:self];
    return self;
}

- (void)release {
    if (__sync_sub_and_fetch(&retainCount, 1) == 0) {
        [methodSignatureForSelector release];
        [forward release];
        object_dispose(self);
    }
}

这段代码确实有效,所以虽然我们不知道在Cocoa类中如何实现retainCount,但可以肯定可以以类似的方式实现。

答案 4 :(得分:0)

要获得更多洞察力,请查看http://www.mikeash.com/pyblog/friday-qa-2011-09-16-lets-build-reference-counting.html,其中Mike Ash探讨了Apple使用的替代实现。