Objective c 其中是存储在目标C中的NSC对象的保留计数

Objective c 其中是存储在目标C中的NSC对象的保留计数,objective-c,memory-management,Objective C,Memory Management,我很好奇保留/发布是如何在内部工作的。从表面上看,似乎有一个整数与NSObject的每个实例相关,当您分别调用-retain和-release时,它会增加和减少 但是看看NSObject,它唯一的实例变量是isa变量,用于确定其类类型 那么,单个对象的保留计数存储在哪里?并不是说我要胡闹,只是为了我自己的启发 它是否与NSObject一起存储,但隐藏在某个Objective C实现细节中?如果是这样,那对我来说似乎是个糟糕的设计。一个人应该能够创建自己的根类,并以类似的方式处理自己的保留/释放计

我很好奇保留/发布是如何在内部工作的。从表面上看,似乎有一个整数与
NSObject
的每个实例相关,当您分别调用
-retain
-release
时,它会增加和减少

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

那么,单个对象的保留计数存储在哪里?并不是说我要胡闹,只是为了我自己的启发


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

听起来不像,但以防万一。。。如果您正在考虑直接使用retaincount

至于实现细节,WWDC 2011年的会议提到,在ARC下,大多数引用计数实现已经转移到ObjC运行时,所以你也许可以自己发现它是如何工作的。对于手动引用计数,大部分ObjC行为都是在CoreFoundation和libdispatch中复制的,它们也是开源的——如果您希望自己实现类似的方案,那么这些可能会很有教育意义


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

我们不知道数据是如何存储的,但我们可以排除一些选项:

私有实现变量 我们可以排除这一点,因为当我们遍历
NSObject
类的IVAR时,我们只看到一个:
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);
关联对象 这一点很难排除,因为没有直接的方法可以获得所有相关对象的列表。然而,由于关联对象的概念是非常新的,并且引用计数一直存在,所以我认为这不太可能

核心基础结构破坏 这是我最好的猜测。创建NSObject时,它是幕后的结构。可以说,实际的NSObject数据表示形式如下:

typedef struct CFObject {
    int retainCount;
    id isa;
} *CFObjectRef;
- (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);
    }
}
然后,在创建对象时:

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;
}

但是,我无法验证这一点,因为还有许多其他方法可以做到这一点(例如,整数到对象的映射)。

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

对于苹果的Objective-C运行时,您可以通过深入研究了解很多

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


但是,有些类重写
retain
和相关方法,并将retain计数存储在其他地方。例如,在调试内存泄漏时,我发现
CALayer
会这样做。而不是使用运行时的正常保留计数机制, Calayer-<代码>将其保留计数存储在私有C++实现对象中。这相当令人沮丧,因为这意味着Instruments Allocations instrument不会记录
CALayer
对象的保留和释放。

不知道这是否相关,但我偶然发现Objective-C中的about higher order messages实现。作者将HOM对象作为根类实现(即,不是从NSObject继承的),实现如下所示:

@interface HigherOrderMessage {
    Class isa;
    NSUInteger retainCount;

    //not relevant to this question part
}
然后,保留计数管理方法的实现方式如下:

typedef struct CFObject {
    int retainCount;
    id isa;
} *CFObjectRef;
- (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,但可以肯定的是,它可以以类似的方式实现。

要了解更多细节,请查看Mike Ash在其中探索了另一种实现,如Apple使用的实现。

当我们谈论对象时,她应该使用ideally talk,意思是我们不应该关心实现细节,而应该关心接口。你提议,苹果应该给我们保留计数的细节,但我说,有了接口定义,我们就有了重新创建它所需的恰到好处的数据量。我们知道我们必须实现
-retain
-release
-dealloc
,…重新创建NSObjetcs内存管理-但我们可以自由地做任何对我们来说最合适的事情。这是一个很好的答案,对其他事情也很有用!除此之外,一个可能的原因是NSConstantString类必须具有非常特定的布局(请参阅)其中不包括retainCount ivar。由于NSConstantString是NSString的一个子类,NSString是NSObject的一个子类,这意味着如果他们给NSObject一个额外的ivar,他们将不得不从头开始构建NSConstantString类,基本上是从另一个基类开始的。这只是一个原因,我相信还有其他原因(更好)那些。@PLPiper实际上,
\uuuu NSCFConstantString
简单地覆盖
保留
释放
,和
自动释放
作为非操作。在调试器中尝试
禁用-n'-[\uu NSCFConstantString保留]
。它还覆盖
重新计数
,以返回
~0
(32位)或