您的位置:首页 > 大数据 > 人工智能

Implementing alloc, retain, release, and dealloc

2013-12-16 11:43 309 查看


Implementing alloc, retain, release, and dealloc

Many parts of OS X and iOS are publicly available as open source software at Apple Open Source.1 As
mentioned above, the alloc, retain, release, and dealloc are methods of the NSObject class.within the Foundation Framework, as part of the Cocoa Framework, Unfortunately, the Foundation Framework is not a part of Apple Open Source. Fortunately, because the
Core Foundation Framework is a part of Apple Open Source, the source code for memory management that is used from NSObject is public. But still, without having the implementation of NSObject itself, it is hard to see the whole picture. So let’s check
an alternative source code from GNUstep.2

GNUstep is a compatible implementation with the Cocoa Framework. Although we can’t expect it to be exactly the same as Apple’s implementation, it works in the same manner and the implementation
should be similar. Understanding GNUstep source code helps us guess Apple’s Cocoa implementation.


The alloc Method

Let’s start with the
alloc
method of the NSObject class in GNUstep. As a side note, some source codes in this book might be modified to make the important part
clear.

The “alloc” method of the NSObject class is called as in:

id obj = [NSObject alloc];


The implementation of
alloc
in NSObject.m is shown in Listing
1–1.

Listing 1–1. GNUstep/modules/core/base/Source/NSObject.m alloc
+ (id) alloc

{

return [self allocWithZone: NSDefaultMallocZone()];

}

+ (id) allocWithZone: (NSZone*)z

{

return NSAllocateObject (self, 0, z);

}


Inside the allocWithZone: method, an object is allocated with the NSAllocateObject function. The implementation is shown in Listing
1–2.

__________

1 Apple, “Apple open source,”
http://opensource.apple.com/


2 GNUstep, “GNUstep.org,”
http://gnustep.org/


Listing 1–2. GNUstep/Modules/Core/Base/Source/NSObject.m NSAllocateObject
struct obj_layout {

NSUInteger  retained;

};

inline id

NSAllocateObject (Class aClass, NSUInteger extraBytes, NSZone *zone)

{

int size = /* needed size to store the object */

id new = NSZoneMalloc(zone, size);

memset(new, 0, size);

new = (id)&((struct obj_layout *)new)[1];

}


The NSAllocateObject function calls NSZoneMalloc to allocate a memory area. After that, the area is filled with zero and the area pointer is returned.

NOTE: Originally, NSZone was used to prevent memory fragmentation (Figure
1–7). By switching zones case by case, memory allocation could be more effective.

But nowadays, Objective-C runtime just ignores zones as you can see at “Transition to ARC Release Notes.”3 Because
a recent runtime memory management algorithm is effective enough, using zones is not considered meaningful or worthwhile because of the complexity.



Figure 1–7. Preventing fragmentation using multiple Zones.

By removing NSZone-related code, the alloc method can be simply rewritten as in Listing
1–3.

__________

3 Apple, “Transition to ARC Release Notes.”,
http://developer.apple.com/library/mac/#releasenotes/ObjectiveC/RN-TransitioningToARC/_index.html


Listing 1–3. GNUstep/modules/core/base/Source/NSObject.m alloc simplified
struct obj_layout {

NSUInteger  retained;

};

+ (id) alloc

{

int size = sizeof(struct obj_layout) + size_of_the_object;

struct obj_layout *p = (struct obj_layout *)calloc(1, size);

return (id)(p + 1);

}


Now that you understand how the alloc method works, let’s move on to
retain
.


The retain Method

The alloc method returns a memory block filled with zero containing a struct obj_layout header, which
has a variable “retained” to store the number of references. This number is called the reference count. Figure
1–8 shows the structure of an object in the GNUstep implementation.



Figure 1–8. Memory image of an object returned by alloc

You can get the reference count value by calling the retainCount method.

id obj = [[NSObject alloc] init];

NSLog(@"retainCount=%d", [obj retainCount]);

/*

* retainCount=1 is displayed.

*/


Just after alloc is called, the reference count is one. The next source code shows how the retainCount function is implemented in GNUstep.

Listing 1–4. GNUstep/Modules/Core/Base/Source/NSObject.m retainCount
- (NSUInteger) retainCount

{

return NSExtraRefCount(self) + 1;

}

inline NSUInteger

NSExtraRefCount(id anObject)
{

return ((struct obj_layout *)anObject)[-1].retained;

}


The source code searches the header from the object pointer and gets the value of the retained variable (Figure
1–9).



Figure 1–9. Accessing the header from an object

Because the memory block is filled with zero when it is allocated, the value of “retained” is
zero. The retainCount function returns 1 by “NSExtraRefCount(self) + 1”. We can guess that the “retain” or “release” method would modify the value by +1 or -1.

[obj retain];


Let’s check the implementation of the “retain” method, as in Listing
1–5.

Listing 1–5. GNUstep/Modules/Core/Base/Source/NSObject.m retain
- (id) retain

{

NSIncrementExtraRefCount(self);

return self;

}

inline void

NSIncrementExtraRefCount(id anObject)

{

if (((struct obj_layout *)anObject)[-1].retained == UINT_MAX - 1)

[NSException raise: NSInternalInconsistencyException

format: @"NSIncrementExtraRefCount() asked to increment too far"];

((struct obj_layout *)anObject)[-1].retained++;

}


Although it has a few lines of code to throw an exception, when the variable “retained” overflows, it is fundamentally incremented by one on the line “retained++”. Next, we
learn the “release” method the functionality of which is opposite to the “retained” method.


The release Method

We can easily guess that the “release” method would have “retained--”. Also, it should have some codes when the value becomes zero.

[obj release];


The “release” method is implemented as shown in Listing
1–6.

Listing 1–6. GNUstep/Modules/Core/Base/Source/NSObject.m release
- (void) release

{

if (NSDecrementExtraRefCountWasZero(self))

[self dealloc];

}

BOOL

NSDecrementExtraRefCountWasZero(id anObject)

{

if (((struct obj_layout *)anObject)[-1].retained == 0) {

return YES;

} else {

((struct obj_layout *)anObject)[-1].retained--;

return NO;

}

}


As we expected, “retained” is decremented by one. If the value is zero, the object will be disposed of by the “dealloc” method. Let’s see how the “dealloc” method is implemented.


The dealloc Method

Listing 1–7 is the implementation of the “dealloc”
method.

Listing 1–7. GNUstep/Modules/Core/Base/Source/NSObject.m dealloc
- (void) dealloc

{

NSDeallocateObject (self);

}

inline void

NSDeallocateObject(id anObject)

{

struct obj_layout *o = &((struct obj_layout *)anObject)[-1];

free(o);

}


It just disposes of a memory block.

We have seen the implementation of alloc, retain, release, and dealloc in GNUstep and have learned the following.

All Objective-C objects have an integer value called the reference count.

The reference count is incremented by one when one of alloc/new/copy/mutableCopy or retain is called.

It is decremented by one when release is called.

Dealloc is called when the integer counter becomes zero.

Next, let’s check the implementation by Apple.

Apple’s Implementation of alloc, retain, release, and dealloc

As I said earlier, the source code of the NSObject class itself is not publicly available. We investigate how the implementation works using an Xcode debugger (lldb)
for iOS application. First, set a breakpoint at the NSObject class method alloc. See what happens on the debugger. The following is the list of functions called inside alloc.

+alloc

+allocWithZone:

class_createInstance

calloc


The NSObject class method alloc calls allocWithZone. After that, through the class_createInstance function, which is documented in the Objective-C Runtime reference, the calloc function is called to allocate the memory block.4 There
does not seem to be much difference with the implementation of GNUstep. We can see the source code of class_createInstance function in objc4 library runtime/objc-runtime-new.mm.5

How about NSObject instance methods retainCount, retain, and release? The following functions are called inside.

-retainCount

__CFDoExternRefOperation

CFBasicHashGetCountOfKey

-retain

__CFDoExternRefOperation

CFBasicHashAddValue
-release

__CFDoExternRefOperation

CFBasicHashRemoveValue


__________

4 Apple, “Objective-C Runtime Reference,”
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html


5 Apple, “Source Browser,”
http://www.opensource.apple.com/source/objc4/


(Also, -dealloc will be called as well when CFBasicHashRemoveValue returns 0.)

In all the above methods, the __CFDoExternRefOperation function is called. Then the function calls similarly named functions. These functions are public. As you can see, if the function names begin with CF, you can find the source
codes in the Core Foundation Framework. 6 The source code in Listing
1–8 is the simplified __CFDoExternRefOperation implementation in CFRuntime.c.

Listing 1–8. CF/CFRuntime.c __CFDoExternRefOperation
int __CFDoExternRefOperation(uintptr_t op, id obj) {

CFBasicHashRef table = get hashtable from obj;

int count;

switch (op) {

case OPERATION_retainCount:

count = CFBasicHashGetCountOfKey(table, obj);

return count;

case OPERATION_retain:

CFBasicHashAddValue(table, obj);

return obj;

case OPERATION_release:

count = CFBasicHashRemoveValue(table, obj);

return 0 == count;

}

}


__CFDoExternRefOperation function is a dispatcher, which calls different functions for retainCount, retain, or release. We can guess these methods would be as follows.

- (NSUInteger) retainCount

{

return (NSUInteger)__CFDoExternRefOperation(OPERATION_retainCount, self);

}

- (id) retain

{

return (id)__CFDoExternRefOperation(OPERATION_retain, self);

}

- (void) release

{

return __CFDoExternRefOperation(OPERATION_release, self);

}


__________

6 Apple, “Source Browser”,
http://www.opensource.apple.com/source/CF/


As you can see in the __CFDoExternRefOperation function above, Apple’s implementation seems to handle the reference count by a hash table (reference counter table) as shown in Figure
1–10.



Figure 1–10. Managing Reference Counts with a hash table

In the GNUstep implementation, reference counts are in the header of each object’s memory block. But in Apple–s implementation, all the reference counts are stored in entries of a hash table. Although
GNUstep implementation looks simpler and faster, there might be some merit to Apple’s as well.

Here are the benefits if reference counts are stored in each object’s header as in GNUstep’s implementation:

Fewer codes.

It is quite simple to manage the lifetime, because each memory area of the reference count itself is included in the object memory area.

How about if reference counts are stored in a hash table as in Apple’s?

Each object doesn’t have a header, thus there is no need to worry about alignment issues for the header area.

By iterating through the hash table entries, memory blocks for each object are reachable.

The latter is especially useful for debugging. When the memory area of some objects is broken and the table is still there, the debugger can reach the objects’ pointers (Figure
1–11).



Figure 1–11. Finding objects in the Reference Count table

Also, to detect memory leaks, instruments check the entries of the table and determine whether someone has ownership of each object.

That’s it for Apple’s implementation. Now that we have a better understanding of how Apple has implemented things, there is one more item to learn regarding memory management in Objective-C: autorelease.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: