目录
什么是自动引用计数
自动引用计数 ARC (Automatic Reference Counting)是指内存管理中对引用采取自动计数的技术。既让编译器来进行内存管理。
在LLVM编译器中设置ARC为有效状态,就无需再次键入retain和release代码了。
满足以下条件就无需手工输入retain和release代码了。
- Xcode4.2或以上版本
- LLVM3.0或以上版本
- 编译器选项中设置ARC有效
内存管理/引用计数
一、概要
生成对象 持有对象 释放对象 废弃对象
二、内存管理的思考方式
1.重要的概念
生成并持有对象 alloc/new/copy/mutableCopy等方法
持有对象 retain
释放对象 release
废弃对象 delloc
负责内存管理的类
2.思考方式
- 自己生成的对象,自己所持有;alloc/new/copy/mutableCopy
alloc
//生成并持有该对象
//指向 生成并持有对象的指针 被赋值给变量 obj
id obj = [[NSObject alloc] init];
new
//生成并持有该对象
id obj = [NSObject new];
copy/mutableCopy
id obj = [NSObjce new];
//生成并持有对象的副本,mutableCopy本质相同
id obj2 = [obj copy];
- 非自己生成的对象,自己也能持有;retain
//取得的对象存在,但自己并不持有
id obj = [NSArray array];
//持有该对象
//通过retain方法,非自己生成的对象跟使用alloc/new/copy/mutableCopy方法生成并持有的对象一样
[obj retain];
- 不再需要自己持有的对象时释放;release
//生成并持有对象的指针 赋值给变量obj
id obj = [[NSObject alloc] init];
//释放持有的对象
//指向对象的指针仍然被保留在obj变量内,貌似能够访问,但一经释放绝不能访问
[obj release];
//清空
obj = nil;
//取得的对象存在,但自己并不持有
id obj = [NSArray array];
//持有该对象
[obj retain];
//释放对象
[obj release];
obj = nil;
如果要用某个方法生成对象,并返回给方法的调用方,源代码该如何实现呢?
-(id)allocObject{
//生成并持有对象
id object = [[NSObject alloc] init];
return object;
}
//调用
//obj 持有对象
id obj = [self allocObject];
类似[NSArray array]方法使得对象存在,但自己不持有对象,又是如何实现的?
-(id)object{
//生成并持有对象
id object = [[NSObject alloc] init];
//取得的对象存在,但自己并不持有
[object autorelease];
//返回
return object;
}
//调用
id obj = [self object];
//要想持有调用retain方法
[obj retain];
release 与 autorelease 的区别
- 无法释放非自己持有的对象
//生成并持有对象
id obj = [[NSObject alloc] init];
//释放对象
[obj release];
//释放之后再次释放已非自己持有的对象,应用程序崩溃
//访问已经废弃的对象时也同样会崩溃
[obj release];
//取得的对象存在,但并不持有
id obj = [NSArray array];
//释放了非自己持有的对象,程序崩溃
[obj release];
三、alloc/retain/release/delloc 实现(GNUstep实现)
1.alloc实现
alloc返回对象的内存图
2.retain的实现
获取对象的retainCount
代码实现
3.release代码实现
4.delloc方法实现
实现总结:
在Object-C的对象中存有引用计数这一整数值。
调用alloc或retain方法后,引用计数加1。
调用release后,引用计数减1。
引用计数为0时,调用delloc方法废弃掉对象。
四、苹果的实现
通过引用计数表管理,该引用计数表是一个散列表,表键值是内存块地址的散列值。
GNU通过内存块头部管理引用计数的好处:
少量代码即可完成;
能够统一管理引用计数用内存块和对象用内存块;
苹果通过引用计数表管理引用计数的好处:
对象用内存块的分布无需考虑内存块头部;
引用计数表各记录中存有内存块地址,可从各个记录追溯到各对象的内存地址。
五、autorelease
autorelease的具体使用方法如下:
- 生成并持有NSAutoreleasePool对象
- 调用已分配对象的autorelease实例方法
- 废弃NSAutoreleasePool对象
示意图如下:
代码如下:
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
id obj = [[NSObject alloc] init];
[obj autorelease];
//等同于 [obj release];
[pool drain];
在cocoa框架中相当于程序的主循环NSRunLoop或者在其它程序可运行的地方,对NSAutoreleasePool对象进行生成、持有、废弃处理。
六、autorelease的实现(GNUstep)
autorelease的伪代码实现
//对象调用autorelease
[obj autorelease];
-(void)autorelease{
[NSAutoreleasePool addObject:self];
}
//NSAutoreleasePool 类内部实现
+(void)addObject:(id)anObj{
NSAutoreleasePool * pool = 取得正在使用的NSAutoreleasePool对象;
if (pool != nil) {
[pool addObject:anObj];
}else{
NSLog(@"NSAutoreleasePool对象非存在状态下调用 autorelease");
}
}
-(void)addObject:(id)anObj{
NSMutableArray * array = NSAutoreleasePool对象的内部数组;
[array addObject:anObject];
}
drain的伪代码实现
//废弃自动释放池
[pool drain];
//NSAutoreleasePool内部实现
-(void)drain{
[self dealloc];
}
-(void)dealloc{
[self emptyPool];
[array release];
}
-(void)emptyPool{
for (id obj in array) {
[obj release];
}
}
七、苹果的实现
苹果的实现原理与GNU类似。
在iOS中可以调用以下方法在控制台输出已被autorelease的对象的状态。
[NSAutoreleasePool showPools];
以下代码会发生异常
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
//运行时出错
//在Foundation框架时,无论调用哪个对象的 autorelease方法实际上调用的都是NSObject对象的autorelease方法。但对于NSAutoreleasePool类,autorelease方法已被该类重载,因此运行时会出错。
[pool autorelease];
行者常至,为者常成!