原文请见:http://qiita.com/ylisr/items/b404c8fc96945a6f613d
本文章是《Pro Multithreading and Memory Management》的读后笔记,包括ARC机制、Blocks和GCD(Grand Central Dispatch)的用法。
Objective-C中的内存管理操作——引用计数(Reference Counting)
Objective-C对象的操作 | Objective-C方法 |
创建及保持对象 | alloc/new/copy/mutableCopy group |
保持对象 | retain |
释放对象 | release |
销毁对象 | dealloc |
+(id)alloc
implementation.m:
+ (id) alloc {
return [self allocWithZone: NSDefaultMallocZone()];
}
+ (id) allocWithZone: (NSZone*)z {
return NSAllocateObject (self, 0, z);
}
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];
}
NSAllocateObject函数调用NSZoneMalloc来分配内存空间。之后空间被0填充,且空间指针被返回。现在,Objective-C忽略曾经用来防止内存碎片化的区域。alloc方法能够被重写为:
demo.m:
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);
}
alloc方法返回一个被0填充、包括一个结构obj_layout头的内存块,它包含一个“retained”变量来存储引用次数。
retain
通过调用retainCount来获取引用计数值:
demo.m:
id obj = [[NSObject alloc] init];
NSLog(@"retainCount=%d", [obj retainCount]);
/* retainCount=1 is displayed. */
retain的执行:
demo.m:
- (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++; }
当“retained”变量溢出时,它便进行递增。
release
demo.m:
- (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;
}
}
“retained”变量进行递减。
dealloc
demo.m:
- (void) dealloc {
NSDeallocateObject (self);
}
inline void NSDeallocateObject(id anObject) {
struct obj_layout *o = &((struct obj_layout *)anObject)[-1];
free(o);
}
内存块实行释放。
autorelease
和C语言中的“automatic variable”类似,当执行离开作用范围时,自动销毁。
autorelease意味着当执行离开程序块时,release方法被自动调用。
demo.m:
- (id) autorelease {
[NSAutoreleasePool addObject:self];
}
ARC(自动引用计数)
使用ARC,“id”和对象类变量必须具有以下持有资格:
_strong,_weak,_unsafe_unretained,_autoreleasing
_strong:持有权被变量作用域/变量间赋值适当地管理。
_weak:用来避免循环引用
该持有资格提供弱引用,弱引用没有对象的持有权。
demo.m:
id __strong obj0 = [[NSObject alloc] init];
id __weak obj1 = obj0;
/* variable obj1 has a weak reference of the created object */
_unsafe_unretained:请勿使用除非你必须支持iOS5之前版本,因为它可能留下一个野指针。请使用_weak来替代。
然而,_weak仅专门应对于应被清零的指针,而不是原始类型。
_unsafe_unretained适用于当你有一个对象指针且ARC无法十分安全地管理内存的情况,比如一个常规C结构的内部。这也是有“在C结构中没有对象指针”限制的理由,除非你用_unsafe_unretained修饰这个指针来让编译器知道你在做什么。同时,它也用来在一些特殊环境下与blocks跳出强引用循环(又称为保留循环)。
但对于99.9%的工作,_weak工作得很好,完全可以忘记_unsafe_unretained。