Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where is the retain count stored for NSObjects in Objective C

I am curious about how retain/release work internally. On the face, it seems like there's an integer related to each instance of an NSObject, which gets increased and decreased when you call -retain and -release, respectively.

But taking a look at NSObject, the only instance variable it has is the isa variable, for determining its class type.

So where are retain counts for individual objects stored? Not that I'm going to muck around with it, but just for my own edification.

Is it stored with the NSObject, but hidden away in some Objective C implementation detail? If so, that seems like a bad design to me. One should be able to create their own root class and handle their own retain/release counting in a similar fashion (not that it's a good idea--one would have to have a very good reason not to use NSObject).

like image 823
Ed Marty Avatar asked Apr 11 '12 16:04

Ed Marty


3 Answers

The storage location for the retain count depends on both the runtime in use and the class implementation.

For Apple's Objective-C runtime, you can figure out a lot by digging into the source code of the Objective-C runtime.

For example, if you're using ARC (and I think even if you're not), the reference counts for most objects are stored in hash tables. Have a look at the _objc_rootRetain function in runtime/objc-arr.mm. I don't know exactly why they did this. Perhaps it's a way of keeping retain counts together for better cache behavior (which is important under ARC because ARC adjusts retain counts more often than non-ARC code usually does).

However, some classes override retain and related methods and store the retain count elsewhere. For example, when debugging a memory leak I discovered that CALayer does this. Instead of using the runtime's normal retain count mechanism, a CALayer stores its retain count in a private C++ implementation object. This is rather frustrating because it means the Instruments Allocations instrument doesn't log retains and releases of CALayer objects.

like image 107
rob mayoff Avatar answered Nov 15 '22 19:11

rob mayoff


We do not know exactly how the data is stored, but we can rule out a few options:

Private Implementation Variables

We can rule this out, simply because when we iterate through the iVars of the NSObject class, we see only one: isa, as shown through this program:

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

And note that even private implementation properties exist in the class metdata.

Private Properties

We can also rule this out, as even private properties are exposed in the classes metadata, as shown by the following example, there are no properties for the NSObject class:

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

Associated Objects

This one is very hard to rule out, as there are no direct ways to get a list of all the associated objects. However, since the concept of associated objects is very new, and reference counting has been around forever, I say that this is not very likely.

CoreFoundation struct-mangling

This is my best guess. When you create a NSObject, it is a struct behind the scenes. What is to say that the actual NSObject data representation is something like this:

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

Then, when an object is created:

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

I cannot verify this however, as there are many other ways this could be done (a map of integers to objects, for example).

like image 33
Richard J. Ross III Avatar answered Nov 15 '22 19:11

Richard J. Ross III


It doesn't sound like it, but just in case... if you're thinking of using retain count directly, don't.

As for implementation details, sessions at WWDC 2011 mentioned that under ARC, most of the reference counting implementation has moved into the ObjC runtime. Source for that is available, so you might be able to find out for yourself how it works. For manual reference counting, much of the ObjC behaviors are replicated in CoreFoundation and libdispatch, which are also open source -- if you're looking to implement a similar scheme yourself, those might prove educational.

In general, this is an implementation detail for the same reason many things are: encapsulation is good policy, especially for framework providers. You don't want users of a framework depending on implementation details because then you can't change your implementation without breaking their code.

like image 23
rickster Avatar answered Nov 15 '22 20:11

rickster