IOS-内存管理
对象所有权与引用计数(转)
当一个所有者(owner,其本身可以是任何一个Objective-C对象)做了以下某个动作时,它拥有对一个对象的所有权(ownership):
1. 创建一个对象。包括使用任何名称中包含“alloc”、“new”、或者“copy”的方法。
2. 保留(retain)一个对象。
一个对象可以有多个所有者,一个所有者也可以拥有多个对象。
相应的,引用计数增减的基本规则是:
1.当所有者创建一个对象时,该对象的引用计数为1。
2.当所有者保留它时,该对象的引用计数加1。
3.当所有者释放(release)它时,该对象的引用计数减1。
与此相关的,当一个所有者对于一个对象的引用计数的增减总计为0时,它放弃了对这个对象的所有权。
现 在我们可以从两个不同的角度来看Objective-C的内存管理问题。从对象所有权的角度来看,当一个对象有着至少一个所有者(owner)的时候,它 依然存在;当它没有任何所有者的时候,它会被释放掉。从引用计数的角度看,一个对象存在时,其引用计数大于零;当一个对象的引用计数为零时,它会调用 dealloc方法并释放掉。这两个角度的关系是:在所有权的背后起作用的机制是引用计数机制;我们通过引用计数的增减来理解所有权的概念;但是你只应当 使用所有权的概念来管理内存,因为如果你试图直接获取对象的引用计数,那么得到的数将让你感到匪夷所思——系统的一些框架会“偷偷”增减对象的引用计数。
回顾一下,内存管理的目标是:
当一个对象的某个所有者依然需要使用它时,保证这个对象的存在;当一个对象的所有所有者都不再需要它时,保证这个对象被销毁。因此只要任何一个所有者在使用完一个对象之后释放掉它,那么以上内存管理的目标就可以实现。
我们可以得出任何一个所有者(记住,所有者本身也只是一个对象)所应当遵守的基本步骤:
拥有一个对象 -> 使用一个对象 -> 放弃对象的所有权。
从引用计数的角度来看就是:
还需要这个对象时,保持对其增减为正;不再需要这个对象时,保持对其增减为0。
下 图很好地诠释了这些基本规则:所有者1和所有者2单独地执行了拥有对象、使用对象、放弃对象所有权的步骤;当所有者1不再需要该对象时及时放弃了所有权, 但此时所有者2依然拥有该对象,因此该对象依然存在,所有者2可以继续使用它;当所有者2也不再需要该对象时,也放弃掉所有权,这时对象以不再有任何所有 者(相应的引用计数也变为0),因此立刻被销毁掉。
引数计数的基本规则
需要注意的是,所有者2只是复制了该对象的指针,并没有使用copy方法,因此复制指针这个操作本身并不增加对象的引用计数;而正因为所有者2希望能使用该对象,因此通过retain方法成为它的所有者,也保证了所有者1放弃该对象时,对象不被销毁。
自动释放与便捷方法(转)
有时候一个所有者创建一个对象后,会立刻将该对象的指针传递给其它所有者。这时,这个创建者不希望再拥有这个对象,但如果立刻给它发送一个 release消息会导致这个对象被立刻释放掉——这样其它所有者还没有来得及保留该对象。解决这个两难问题的方法是,给对象发送一个 autorelease消息:这样创建者不再拥有该对象的所有权;该对象成为自动释放的对象,但是不会立刻被释放掉;其它所有者可以有时间保留或复制该对 象,并成为其唯一所有者。
我们来看一个自动释放的例子(代码清单3-1)。一个所有者先用alloc方法创建一个对象;此时 该所有者拥有这个对象,对象的引用计数为1。紧接着,所有者自动释放该对象;所有者此时已经放弃了所有权,但对象的引用计数在一段时间内依然为1。我们可 以看出自动释放的另一个好处:你不会因为在后面忘记给对象发送release消息而造成内存泄露。
代码清单3-1
-(Object*)returnAutoreleaseObject {
Object* obj = [[Object alloc] init];
return [obj autorelease];
}
与自动释放相关的,有一大类构造方法(constructor method),由它们构造的对象直接就是自动释放的对象;这一类构造方法叫做便捷方法。比如下面这句的字符串就是一个自动释放的对象,stringWithFormat:就是一个便捷方法。
NSString* string = [NSString stringWithFormat:@”autoreleaseString”];
再举几个便捷方法的例子,方便读者以后的开发。
1.NSArray的arrayWithObjects:和arrayWithArray:。
2.UIImage的imageNamed:。
3.NSNumber的numberWithBool等。
现在我们已经解释了,autorelease方法会在一段时间以后释放掉一个对象,在这段时间内我们可以安全地使用该对象。那么这段时间究竟是多久呢?我们需要先更多地了解自动释放的机制,再来回答这个问题。
让 我们先来看看自动释放池。自动释放池是NSAutoreleasePool的实例,其中包含了收到autorelease消息的对象。当一个自动释放池自 身被销毁(dealloc)时,它会给池中每一个对象发送一个release消息(如果你给一个对象多次发送autorelease消息,那么当自动释放 池销毁时,这个对象也会收到同样数目的release消息)。可以看出,一个自动释放的对象,它至少能够存活到自动释放池销毁的时候。
那么 自动释放池何时被创建,又何时被销毁呢?在每一个事件周期(event cycle)的开始,系统会自动创建一个自动释放池;在每一个事件周期的结尾,系统会自动销毁这个自动释放池。一般情况下,你可以理解为:当你的代码在持 续运行时,自动释放池是不会被销毁的,这段时间内你也可以安全地使用自动释放的对象;当你的代码运行告一段落,开始等待用户输入(或者其它事件)时,自动 释放池就会被释放掉,池中的对象都会收到一个release消息,有的可能会因此被销毁。
到此为止,相信你已经对自动释放的机制有了一个大 体的了解。自动释放而非直接释放,可以帮助你节省一些代码量,提高开发速度。但是它有一个直接的缺点:它延缓了对象的释放,在有大量自动释放的对象时,会 占用大量内存资源。因此,你需要避免将大量对象自动释放。并且,在以下两种情况下,你需要手动建立并手动销毁掉自动释放池:
1.当你在主线程外开启其它线程时:系统只会在主线程中自动生成并销毁掉自动释放池。
2.当你在短时间内制造了大量自动释放对象时:及时地销毁有助于有效利用iPad上有限地内存资源。