OC基础回顾(八)内存管理

前言:

内存管理是程序设计中常见的资源管理的一部分。
如果只分配而不释放内存,就会发生内存泄露(leak memory),即程序的内存占用量不断增加,最终会被耗尽并导致程序崩溃。
不要使用任何刚被释放的内存,否则可能误用陈旧的数据,从而引发各种各样的错误,而且,如果该内存已经加载了其他数据,将会破坏这些新的数据。

程序运行过程中需要创建大量的对象,在Objective-C中,对象是存放在堆中的,系统并不会自动释放堆中的内存。其他高级程序语言,如C#、Java都是通过垃圾回收机制(GC)来解决这个问题的,而Objective-C没有类似的垃圾回收机制,因此它的内存管理需要开发人员手动维护。

1.对象生命周期

对象的生命周期包括诞生(通过alloc或new方法实现)、生存(接受消息并执行操作)、交友(通过复合以及向方法传递参数)以及最终死去(被释放掉)。当对象的生命周期结束时,内存将被收回以供其他对象使用。

1.1 引用计数

Cocoa采用一种叫做引用计数(reference counting)的技术,有时也叫做保留计数(retain counting)。每个对象都有一个与之相关联的整数,被称作它的引用计数或保留计数。

当使用alloc、new方法或者通过copy消息创建一个对象时,retain counting 被置为1.

-(id) retiain;
给对象发送一条retain消息,retain counting的值加1.

-(oneway void) release;
给对象发送一条release消息,retain counting的值减1.

当一个对象因其retain counting归0而即将被销毁时,Objective-C会自动向对象发送一条dealloc消息。
可以重写dealloc方法,来释放已经分配的全部相关资源。不能直接调用dealloc方法,Objective-C会在需要销毁对象时自动调用它。

-(NSUInteger) retainCount;
获得引用计数当前的值

1.2 对象所有权    

如果创建或者复制某个对象时,则拥有了该对象的所有权:
alloc,allocWithZone,copy,copyWithZone,mutableCopy,mutableCopyWithZone

如果没有创建对象,而是将对象保留使用,同样拥有该对象的所有权:
retain

如果你拥有了某个对象的所有权,在不需要某一个对象时,需要释放它们:
release,autorelease


1.3 重写dealloc方法

可以用dealloc方法来查看一个对象是否被回收,如果没有被回收则有可能造成内存泄露。如果一个对象被释放,那么最后引用它的对象我们手动设置为nil,否则可能造成野指针错误。需要注意,在Objective-C中给空对象发送消息是不会引起错误的。

1.4 自动释放池

大家都知道不再使用一个对象时应该将它释放,但是弄清楚什么时候不再使用一个对象并不容易。自动释放池就是用来解决这类问题的。
自动释放池(autorelease pool)提供了一个对象容器,每次对象发送autorelease消息时,对象的引用计数并不真正变化,而是向自动释放池中添加一条记录,当自动释放池发送drain或者release消息时,会通知池中的所有对象发送release消息,从而真正将对象的引用计数减少。实际上是延迟了释放。

2.Cocoa的内存管理规则

规则如下:

1)当你使用new、alloc或者copy方法创建一个对象时,该对象的保留计数的值为1.当不再使用该对象时,应该向对象发送一条release或者autorelease消息。这样,该对象将在其使用寿命结束时被销毁。

2)当你通过其他方法获得一个对象时,假设该对象的保留计数的值为1,而且已经被设置为自动释放,那么你不需要执行任何操作来确保该对象得到清理。如果你打算在一段时间内拥有该对象,则需要保留它并确保在操作完成时释放它。

3)如果你保留了某个对象,就需要(最终)释放或自动释放该对象。必须保持retain方法和release方法的使用次数相等。

2.1 临时对象

NSMutableArray *array = [[NSMutableArray alloc] init];// count : 1
//use the array
[array release];// count : 0
上例中使用alloc获得一个array,所以需要安排该对象的释放

NSMutableArray *array = [NSMutable arrayWithCapacity:17];
//count : 1, autorelease
//use the array
arrayWithCapacity:方法与alloc、new、copy这三个方法不同,因此可以假设该对象被返回时保留计数的值为1并且已经被置为自动释放。当自动释放池被销毁时,向array对象发送release消息,该对象的保留计数的值为0,其占用的内存被回收。

使用NSColor类对象的部分代码如下:
NSColor *color = [NSColor blueColor];
blueColor方法也不属于retain、new、copy这三中方法。blueColor方法返回一个全局单例(singleton)对象——每个需要访问它的程序都可以共享一个单一对象,这个对象永远不会被销毁。你不需要关心其实现细节,只需知道你不用自己动手释放color。


2.2 垃圾回收机制

     Objective-C 2.0引入了自动内存管理机制,也称为垃圾回收机制。
     对于已经创建的对象,当你忘记清理时,系统会自动识别哪些对象仍在使用,哪些对象可以回收。
     
     注意:垃圾回收机制只支持OS X应用开发,无法用在iOS应用程序上。实际上,在iOS开发中,苹果公司建议不要在自己的代码中使用autorelease方法,也不要使用会返回自动释放对象的一些便利方法,一般这些便利方法都会返回一个新对象的类方法。比如NSString,所有以stringWith开头的方法都是便利方法。

2.3  ARC

    此小节扩展文章:《理解Objective-C中的ARC》
     垃圾回收机制会对移动设备的可用性产生非常不利的影响,因为移动设备比电脑更私人化,资源更少。用户可不想在玩游戏或者打电话的时候因为系统突然进行内存清理而卡住。
     苹果公司的解决方案被称为自动引用计数(automatic reference counting,ARC)。ARC会追踪你的对象并决定哪一个仍会使用哪一个不会再用刀。如果启用了ARC,只管向平常那样按需分配并使用对象,编译器会帮你插入retain和release语句,无需自己动手。
     ARC与垃圾回收机制的不同:垃圾回收机制在运行时工作,通过返回的代码来定期检查对象。ARC是在编译时进行工作。

3.异常

    【 此小节扩展文章:《自定义异常或者扩展》
     Cocoa使用NSException类来表示异常。异常处理的真正目的是处理程序中生存的错误。Cocoa框架处理错误的方式通常是退出程序,这不是你想要的方式。为了找到出错的原因,应该抛出并捕捉代码中的异常。
     可以在Xcode中启用Enable Objective-CExceptions项。

如果一个异常被抛出但没有被捕捉到,程序会在异常断点处停止运行并通知有这个异常。

3.1 与异常有关的关键字

@try:定义用来测试的代码块以决定是否要抛出异常。
@catch:定义用来处理已抛出异常的代码块。接受一个参数,通常是NSException类型,但也可能是其他类型。
@finally:定义无论是否有抛出异常都会执行的代码块,这段代码总会执行。
@throw:抛出异常。

代码:
@try
{
          //用来测试的代码块
}
@catch(NSException *exception)
{
          //处理抛出的异常
}
@finally
{
          //总会执行的代码块
          //会在@throw引发下一个异常处理调用之前执行代码,因为@finally是在@throw发生之前调用的。
}

3.2 捕捉不同类型的异常

          可以根据需要处理的异常类型使用多个@catch代码块。处理代码应该从具体到抽象的顺序排列,并在最后使用一个通用的处理代码:
@try{
}@catch (MyCustomException *custom){
}@catch (NSException *exception){
}@catch (id value){
}@finally{
}


3.3 抛出异常

当程序检测到了异常,就必须向处理异常的代码块报告这个异常。
程序会创建一个NSException实例来抛出异常,并使用以下技术 之一
1)使用“@throw异常名;”语句抛出异常。
2)向某个NSException对象发送raise消息。

如:
NSException *theException = [NSException exceptionWithName:…];
//抛出异常
@throw theException;
或者
[theException raise];

注意:两种方法只可选其一,raise只对NSException对象有效,而@throw也可以用在其他对象上。

通常在异常处理代码中抛出异常,也可以重复抛出异常而无需指定异常对象。

3.4 异常也需要内存管理

在@finally代码块中做清理工作。
-(void)mySimpleMethod
{
     NSDictionary *dic = [[NSDictionary alloc] initWith: …];
     @try{
          [self processDictionary : dic];
     }
     @finally{
          [dic release];
     }
}


3.5 异常和自动释放池

因为@finally代码块会在异常抛出之前执行代码,会导致pool的释放早于异常通知,变成可怕的僵尸异常(zombie exception)。以下方法可以解决:在pool外保留它。
代码:
-(void)myMethod
{
     id savedException = nil;
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
     NSDictionary *myDictionary = [[NSDictionary alloc] initWithObjectsAndKeys:@“asdfs”,nil];
     @try
     {
          [self processDictionary : myDictionary];
     }
     @catch(NSException *e)
     {
          savedException = [e retain];
          @throw;
     }
     @finally
     {
          [pool release];
          [savedException autorelease];
    }
}






   







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值