Objective-C中的内存管理方法基本上和C++中的是一样的,也是采用引用计数这一概念,不过在C++中更多的是使用智能指针,而OC中采用的是自动释放池这一方法。内存泄漏是任何需要自己管理内存的语言必须得解决的问题,程序的内存占用量如果持续的增加,那最终会消耗掉所有的内存,导致内存崩溃。同时,在OC中也需要注意,不要使用任何刚刚释放掉的内存,否则可能会误用陈旧的数据,从而引发各种各样的错误,而且如果该内存已经加载了其他的数据,将会破坏这些新数据。
对象的生命周期包括诞生(通过alloc或new方法事项)、生存(接收消息并执行操作)、交友(通过符合以及向方法传递参数)、以及最终死去(被释放掉)。对象的生命期结束后内存将被回收以供新的对象使用。有关new和alloc的区别可以参考下这里:http://mobile.51cto.com/hot-404891.htm
当一个对象被创建使用后,我们怎么来知道该对象的生命周期是否结束了呢?在OC中同样也是采用引用计数的技术或者叫保留计数,引用计数的概念和C++中的引用计数都是差不多的。当某段代码需要访问一个对象时,该代码会将保留计数器的值增加1,当该代码结束访问时,相应的会减去1。当使用alloc、new方法或者通过copy消息(接收到消息的对象会创建一个自身的副本)创建对象时,对象的引用计数值被设置为1。要增加引用计数器的值,可以给对象发送一条retain消息,要减少的话,可以给对象发送一条release消息。
当一个对象因为其保留计数器的值归0而即将被销毁时,OC会自动向对象发送一条dealloc消息。可以在自己的对象中重写dealloc方法,这样就能释放掉该对象中已经分配的资源。不过一定不要直接调用dealloc方法,oc会在需要销毁对象时自动调用它。
要获得引用计数的当前值,可以发送retainCount消息。retain、release、retainCount的方法声明如下:
-(id)retain;
-(oneway void)release;
-(NSUInteger)retainCount;
retain方法返回一个id类型的值。通过这种方式,可以再接收其他消息的同时进行retain调用,增加对象的保留计数器的值并要求对象执行某种操作。例如:
[[car retain] setTire: tire atIndex:2];
和C++一眼,OC中也有对象所有权的管理,在C++中,创建了一个对象后,可能会在很多地方使用该对象,在某一个代码区域中使用完该对象后不能直接就把该对象释放掉,因为该对象可能还在其他的代码区域中使用,如果在某处释放该对象后,那其他使用该对象的地方势必会出现错误,在C++中也是通过引用计数来对该对象进行管理,当该对象的引用计数减为0后,则表明没有地方在使用该对象了,所以可以释放该对象,反映到具体的方法则采用的是智能指针,C++11中引入了shared_ptr<>,weak_ptr<>等智能指针来进行对象的管理。
在OC中采用的对象管理方法则要比C++中的更加方便了,OC中有一个自动释放池(autorelease pool)的概念,顾名思义,表示的是一个用来存放对象的pool,并且能对pool中对象占用的内存进行自动释放,不需要你去操太多的心。
NSobject类提供了一个叫做autorelease的方法:
-(id)autorelease;
该方法预先设定了一条会在未来某个时间点发送的release消息,其返回值是接收这条消息的对象。当给一个对象发送autorelease消息时,实际上是将给对象添加到了自动释放池中。当自动释放池被销毁时,会向该池中的所有对象发送release消息。
有两种方法可以创建自动释放池:通过@autoreleasepool关键字和通过NSAutoreleasePool对象。
当你使用@autorelease{}时,所有大括号里的代码都被放入这个新池里了。如果采用的NSAutoreleasePool对象创建一个自动释放池,那创建和释放NSAutoreleasePool对象之间的代码就会使用这个新的池子。使用@autoreleasepool关键字的方式,不需要你去创建和释放自动释放池对象,但如果使用的是NSAutoreleasePool对象的方式,则需要自己创建和释放自动释放池对象。
如下代码实例了两种创建自动释放池的方法:
int main (int argc, const char * argv[])
{
NSAutoreleasePool *pool;
pool = [[NSAutoreleasePool alloc] init];
RetainTracker *tracker;
tracker = [RetainTracker new]; // count: 1
[tracker retain]; // count: 2
[tracker autorelease]; // count: still 2
[tracker release]; // count: 1
NSLog (@"releasing pool");
[pool release];
// gets nuked, sends release to tracker
@autoreleasepool
{
RetainTracker *tracker2;
tracker2 = [RetainTracker new];
[tracker2 retain];
[tracker2 autorelease];
[tracker2 release];
NSLog(@"auto releasing pool");
}
return (0);
}
注意当把对象放入自动释放池时,对象的保留计数器的值是不会改变的,如上图代码中的[tracker autorelease]; 上面代码中当采用NSAutoreleasePoll对象来创建自动释放池是是把NSAutoreleasePool当做一个普通的对象来处理的,所有有对该对象的创建和释放过程,当tracker的保留计数器的值减到1后,该对象还处于活跃的状态,但是接下来销毁自动释放池[pool release] 后,该对象也会收到一条release消息,则对象被销毁。
Cocoa的内存管理规则如下:
当你使用new、alloc和copy方法创建一个对象时,该对象的保留计数器的值为1.当不在使用该对象时,你应该对该对象发送一条release或者autorelease消息。这样,该对象将在其使用寿命结束时被销毁。
当你通过其他方法获得一个对象时,假设该对象的保留计数器的值为1,而且已经设置为自动释放,那么你不需要执行如何操作来确保该对象得到清理。如果你打算在一段时间内拥有该对象,则需要保留它并确保在操作完成时自动释放它。
如果你保留了某个对象,就需要释放或自动释放该对象。必须保持retain方法和release方法的使用次数相等。
对于临时拥有某个对象,如果是用new allo 或copy方法获得一个对象,则只需安排好该对象的内存释放,通常使用release来实现。如果是采用的类似arrayWithCapacity:方法来获得对象,则可以理解为该对象被返回时保留计数器的值为1且已经被设置为自动释放了。
对于长期拥有对象,如果使用的是new alloc或copy方法获得一个对象,则不需要执行任何操作,该对象的保留计数器值为1,因此将一直存在着,所有只需确保在拥有该对象的dealloc方法中释放它即可。如果使用的是除了new alloc或copy方法来获得一个对象,则需要记得保留该对象。在对象时用结束后,需要在自己的dealloc方法中释放它。
有一点需要记住,自动释放池被清理的时间是完全确定的:要么是在代码中你自己手动销毁,要么是使用AppKit时在事件循环结束时销毁。所以,你不必担心程序会随机的销毁自动释放池,也不必保留使用的每一个对象,因为在调用函数的过程中自动释放池是不会销毁的。
垃圾回收功能类似于java中的垃圾回收机制,但是垃圾回收机制只支持os x引用开发,无法用在ios应用程序上。
因为在移动设备上的资源少,垃圾回收机制会对移动设备的可用性产生非常不利的影响,所以为了解决这一问题,有了自动引用计数。如果你启动了自动引用计数(ARC),则只管像平常那样按需分配并使用对象即可,编译器会帮你插入retain和release语句,无需你自己动手。不过ARC的使用时有一些条件的,在使用的时候得先满足这些条件。具体使用需查看相应的资料。
参考书籍:《Objective-C 基础教程(第二版)》