本文参考:《objective-c高级编程》
一、引用计数式内存管理的思考方式
①自己生成的对象,自己持有
②非自己生成的对象,自己也能持有
③不再需要自己持有的对象时,释放
④非自己持有的对象,无法释放
以下逐一做讲解:
①自己生成的对象,自己持有
/* alloc
* new
* copy
* mutableCopy
*/
//自己生成,并持有对象
id obj1 = [[NSObject alloc] init];
id obj2 = [NSObject new];
②非自己生成的对象,自己也能持有
//取得非自己生成并持有的对象,obj不持有该数组
id obj = [NSMutableArray array];
//obj持有数组
[obj retain];
③不再需要自己持有的对象时,释放
id obj1 = [[NSObject alloc] init];
[obj release];
④非自己持有的对象,无法释放
id obj1 = [[NSObject alloc] init];
[obj release];
[obj release]; //wrong
⑤autorelease
//如果要用某个方法生成对象,并将其返还给该方法的调用方。
- (id)allocObject {
//自己生成并持有对象
id obj = [[NSObject alloc] init];
return obj;
}
//注意命名规则,不能加alloc
- (id)Object {
//自己生成并持有对象
id obj = [[NSObject alloc] init];
//取得的对象存在,但自己不持有对象。autorelease使对象在超出指定的生存范围时能够自动并正确的释放(调用release方法)
[obj autorelease];
return obj;
}
使用以上两种方式的区别:
{
[obj allocObject];
}
{
[obj object];
[obj retain];
}
二、alloc/retain/release/dealloc的实现
NSObject类的Foundation框架没有公开。此处是使用GNUstep来参考的(它是Cocoa框架的互换框架)
1.先看看GNUstep内部的实现(简单说明):
①alloc:
alloc—>allocWithZone—>NSAllocateObject
NSAllocateObject:(通过NSZoneMalloc函数分配存放对象所需的内存空间,之后将该内存空间置0,最后返回作为对象而使用的指针)
NSZone:为了防止内存碎片化而引入的结构,对内存分配的区域本身进行多重化管理,根据使用对象的目的、对象的大小分配内存,提供了使用效率。
结论:
alloc类方法中,用一个结构体中的NSUInteger retained来保存引用计数,并将其写入对象内存的头部。
②retain/release
③dealloc
dealloc—>NSDeallocateObject(self)
2.苹果的实现
苹果内部不是把retain值写在对象的头部,它是采用引用计数表来管理引用计数,两者对比一下:
①写在头部:
a)少量代码完成
b)能够统一管理引用计数和对象使用的内存块
②引用计数表
a)分配内存时无需考虑内存块头部
b)表中存有内存块地址,可以从各个记录追溯到各个对象的内存块(这个很好,当出现故障使内存块损坏时,可以通过表来确认内存块的位置)
三、详细解释autorelease
1.首先看看C语言:
{
int a;
}
//超出{}作用域后,变量a被废弃,不可访问
2.OC中类似作用域的东西:NSAutoreleasePool
作用:相当于{},废弃NSAutoreleasePool对象时,调用它内部的对象的release方法
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id obj = [[NSObject alloc] init];
[obj autorelease];
[pool drain];
PS:大量的创建NSAutoreleasePool对象,但是不废弃它,它内部的对象就不会被释放。有时会造成内存不足的现象(例如,读入大量图片的同时改变尺寸)
3.autorelease的实现
①先看看GUNstep如何实现的
autorelease实例方法的本质就是调用NSAutoreleasePool对象的addObject方法
-(id) autorelease {
[NSAutoreleasePool addObject:self];
}
//GNUstep中使用的是连接链表,就好比在NSMutableArray中添加一个对象
-(void) addObject:(id) obj {
[array addObject:obj];
}
//当调用drain方法时
[pool drain];
-(void) drain {
[self dealloc];
}
-(void) dealloc {
[self emptyPool];
[array release];
}
-(void) emptyPool {
[obj release];
}
②苹果的实现和以上原理相同,此处不讲了。
PS:
//发生异常
//无论哪个对象调用autorelease,实际上都是调用NSObject类的autorelease实例方法。但是对于NSAutoreleasepool,这个实例方法已经被它重载,运行时就会出错。
NSAutoreleasepool *pool = [[NSAutoreleasepool alloc] init];
[pool autorelease];