OC的内存管理

内存管理四句箴言:

自己生成的自己持有
非自己生成的对象自己也可持有
不再需要自己持有时释放
非自己持有的对象无法释放

Objective-C中的内存管理机制跟C语言中指针的内容是同样重要的,要开发一个程序并不难,但是优秀的程序则更测重于内存管理,它们往往占用内存更少,运行更加流畅。在Xcode4.2及之后的版本中由于引入了ARC(Automatic Reference Counting)机制,程序编译时Xcode可以自动给你的代码添加内存释放代码。内存管理是开发中不可忽略的一块,虽然ARC帮我们节省了很多精力,不过作为一名合格的开发人员,最基本的知识还是要理解的。

自持与不自持

在开发中,如果使用了alloc,new,copy,mutableCopy或以其开头的方法名,意味着自己生成的对象只有自己持有,如:allocMyJob,newItName,copyAfter,mutableCopyYourName。但是以allocate,newer,copying,mutableCopyed开头的是不自己持有的,很明显它们是假冒的。

以下为例:

id obj = [NSMutableArray array]; // 取得非自己生成并持有的对象

取得的对象存在,但自己并不持有对象。NSMutableArray类对象被赋给变量obj,但变量obj自己并不持有该对象。持有时需要用retain方法,如:

id obj = [NSMutableArray array]; 
[obj retain];

通过retain,非自己用alloc等生成的对象也可以自己持有了。

不需要时,使用release释放,对象一经释放就不可访问了。用alloc/new/copy/mutableCopy持有和retain持有的对象,不需要时一定要用release释放。

同理使用如allocObject也可以取得自己持有对象。

id obj1 = [obj0 allocObject];

当使用autorelease时,即是取得了对象存在,但是自己不持有对象。如:

- (id)object
{
id obj = [[NSObject alloc] init]; //自己持有对象
[obj autorelease];
return obj;// 取得的对象存在,但自己不持有
}

autorelease

autorelease

[NSMutableArray array]就是因为用了autorelease使得谁都不持有的。当然再次使用retain就持有了。程序里的所有autorelease pool是以桟(stack)的形式组织的。新创建的pool位于桟的最顶端。当发送autorelease消息给一个对象时,这个对象被加到栈顶的那个pool中。发送drain给一个pool时,这个pool里所有对象都会受到release消息,而且如果这个pool不是位于栈顶,那么位于这个pool“上端”的所有pool也会受到drain消息。

[pool drain] 和 [pool release] 的区别:
release,在引用计数环境下,由于NSAutoReleasePool是一个不可以被retain的类型,所以release会直接dealloc pool对象。当pool被dealloc的时候,pool向所有在pool中的对象发出一个release的消息,如果一个对象在这个pool中autorelease了多次,pool对这个对象的每一次autorelease都会release。在GC(garbage-collected environment)环境下release是一个no-op操作(代表没有操作,是一个占据进行很少的空间但是指出没有操作的计算机指令)。

drain,在引用计数环境下,它的行为和release是一样的。在GC的环境下,这个方法调用objc_collect_if_needed 触发GC。重点是:在GC环境下,release是一个no-op,所以除非你不希望在GC环境下触发GC,你都应该使用drain而不是使用release来释放pool。原文如下:

drain

In a reference-counted environment, releases and pops the receiver; in a garbage-collected environment, triggers garbage collection if the memory allocated since the last collection is greater than the current threshold. - (void)drain

Discussion
In a reference-counted environment, this method behaves the same as release. Since an autorelease pool cannot be retained (see retain
), this therefore causes the receiver to be deallocated. When an autorelease pool is deallocated, it sends a release message to all its autoreleased objects. If an object is added several times to the same pool, when the pool is deallocated it receives a release message for each time it was added.In a garbage-collected environment, this method ultimately calls objc_collect_if_needed.

Special Considerations
In a garbage-collected environment, release is a no-op, so unless you do not want to give the collector a hint it is important to use drain in any code that may be compiled for a garbage-collected environment.

对于iOS来说drain和release的作用其实是一样的。

一个对象被加到一个pool很多次,只要多次发送autorelease消息给这个对象就可以;同时,当这个pool被回收时,这个对象也会收到同样多次release消息。简单地可以认为接收autorelease消息等同于:接收一个retain消息,同时加入到一个pool里;这个pool用来存放这些暂缓回收的对象;一旦这个pool被回收(drain),那么pool里面的对象会收到同样次数的release消息。-UIKit框架已经帮你自动创建一个autorelease pool。大部分时候,你可以直接使用这个pool,不必自己创建;所以你给一个对象发送autorelease消息,那么这个对象会加到这个UIKit自动创建的pool里。

某些时候,可能需要创建一个pool:
1.没有使用UIKit框架或者其它内含autorelease pool的框架,那么要使用pool,就要自己创建。

2.如果一个循环体要创建大量的临时变量,那么创建自己的pool可以减少程序占用的内存峰值。(如果使用UIKit的pool,那么这些临时变量可能一直在这个pool里,只要这个pool受到drain消息;完全不使用autorelease pool应该也是可以的,可能只是要发一些release消息给这些临时变量,所以使用autorelease pool还是方便一些)

3.创建线程时必须创建这个线程自己的autorelease pool。-使用alloc和init消息来创建pool,发送drain消息则表示这个pool不再使用。pool的创建和drain要在同一上下文中,比如循环体内。

结语

ObjC中没有垃圾回收机制,在ObjC中内存的管理是依赖对象引用计数器来进行的:在ObjC中每个对象内部都有一个与之对应的整数(retainCount),叫“引用计数器”,当一个对象在创建之后它的引用计数器为1,当调用这个对象的alloc、retain、new、copy方法之后引用计数器自动在原来的基础上加1(OC中调用一个对象的方法就是给这个对象发送一个消息),当调用这个对象的release方法之后它的引用计数器减1,如果一个对象的引用计数器为0,则系统会自动调用这个对象的dealloc方法来销毁这个对象。手动管理内存有时候并不容易,因为对象的引用有时候是错综复杂的,对象之间可能互相交叉引用,此时需要遵循一个法则:谁创建,谁释放