从C/C++到Objective-C(三)--- 内存管理

来源:互联网 发布:用户手册 软件功能 编辑:程序博客网 时间:2024/05/16 07:21

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 基础教程(第二版)》

0 0
原创粉丝点击