7. 内存管理

来源:互联网 发布:html5音乐播放器源码 编辑:程序博客网 时间:2024/06/05 15:29

   1.Cocoa采用了一种叫做引用计数(reference counting)的技术,有时也叫做保留计数。每个对象都有一个与之相关联的整数,被称作它的引用计数器或保留计数器。当某段代码需要访问一个对象时,该代码就将该对象的保留计数器值加1,表示“我要访问该对象”。当这段代码结束访问时,将对象的保留计数器减1,表示不再访问该对象。当保留计数器的值为0时,表示不再有代码访问该对象了,因此它将被销毁,其占用的内存被系统回收以便重用。

   当使用alloc、new方法或者通过copy消息创建一个对象时,对象的保留计数器值被置为1.要增加对象保留计数器的值,可以给对象发送一条retain消息。要减少的话,可以给对象发送一条release消息。

   当一个对象因其保留计数器归0而即将被销毁时,Objective-C会自动向对象发送一条dealloc消息。一定不要直接调用dealloc方法,objective-C会在需要销毁对象时自动调用它。

  要获得保留计数器当前的值、可以发送retainCount消息。下面是retain、release和retainCount的方法声明。

   - (id) retain;

   - (oneway void) release;

   -   (NSUInteger) retainCount;

    2.自动释放

     NSObject类提供了一个叫做autorelease的方法:

     - (id) autorelease;该方法预先设定了一条会在未来某个时间发送的release消息,其返回值是接收这条消息的对象。当给一个对象发送autorelease消息时,实际上是将该对象添加到了自动释放池中。当自动释放池销毁时,会向该池中的所有消息发送release消息。

         2.1自动释放池创建的两种方法:

             (1)、通过@autoreleasepool关键字。

             (2)、通过NSAutoreleasePool对象。

                当使用@autorelease{}时,所有在花括号里的代码都会被放入这个新池子里。当程序运算是内存密集型的,可以使用这种自动释放池。任何在花括号里定义的变量在括号外就无法使用了。

                 第二种更加明确的方法就是使用NSAutoreleasePool对象。当时用这个方法时,创建和释放NSAutorelease对象之间的代码就会使用这个新的池子。

                  NSAutoreleasePool *pool;

                  pool = [NSAutoreleasePool new];

                  ...

                  [pool release];

           2.2Cocoa的内存管理规则

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


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

             2.3自动引用计数

             如果保留了某个对象,就需要释放或自动释放该对象。必须保持retain方法额release方法的使用的词数相等。

              在IOS中无法使用垃圾回收,苹果公司的解决方案被称为自动引用计数(automatic reference counting,ARC)。ARC不是垃圾回收器。垃圾回收器在运行时工作,通过返回的代码来定期检查对象。与此相反,ARC实在编译时进行工作。它在代码中插入了合适的retain和release语句,就好像自己动手写好了所有的内存管理代码。

   说明:ARC只对可保留的对象指针(ROPs)有效。可保留的对象指针主要有以下三种:

    (1) 代码块指针

    (2)Objective-C对象指针

     (3)通_attribute_((NSObject))类型定义的指针

   使用ARC的三个条件

    (1)能够确定哪些对象需要进行内存管理

    (2)能够表明如何处理对象

    (3)有可行的办法传递对象的所有权

    第一个条件是对象的最上层集合知道如何去管理它的子对象。比方说你有一个通过malloc:方法创建的字符串数组:

         NSString **myString;

         myString = malloc(10* sizeof(NSString *));

   这段代码创建了一个指向10个字符串的C型数组。因为C型数组不是可保留的对象,所以无法在这个结构体里使用ARC特性。

    第二个条件是必须能够对某个对象保留计数器的值进行加1或减1的操作。也即是说所有NSObject类的子类都能进行内存管理。

   第三个条件是在传递对象的时候,你的程序需要能够在调用者和接收者之间传递所有权。

    2.4Weak(弱引用)

      声明若引用的两种方式:声明变量时使用_weak关键字或对属性使用weak特性。

     _weak NSString *myString;

     @property(weak) NSString *myString;

    使用ARC的时候有两种命名规则需要注意:

     (1)、属性名称不能以new开头,比如说@property NSString *newString 是不允许的。

      (2)、属性不能只有一个read-only而没有内训管理特性。如果你没有启用ARC,可以使用@property(readonly)NSString *title语句,但如果你启用了ARC功能,就必须制定由谁来管理内存。因为默认的特性是assign,所以进行一个简单的修复,使用unsafe_unretained就可以了。

  3.异常

   异常所有的关键字都是以@开头的。以下是它们的各自左右。

   @try:定义用来检测的代码块以决定是否要抛出异常。

   @catch():定义用来处理已抛出异常的代码块。接受一个参数,通常是NSExecption类型,但也可能是其他类型。

   @finally:定义无论是否有抛出异常都会执行代码块,这段代码块总是执行的。

   @throw:抛出异常。

   3.1抛出异常

      当程序检测到了异常,就必须向处理它的代码块报告这个异常。

       程序会创建一个NSExecption实例来抛出异常,并会使用以下两种技术之一:

       (1)使用“@”throw异常名";语句来抛出异常;

       (2)向某个NSException对象发送raise消息。例如:

        NSExecption *theException = [NSException exceptionWithName:...];

         要抛出这个异常可以用这个语句

          @throw theException;

        或者用

         [theException raise];

         两种方法不能同时使用。两种方法的区别是raise只对NSException对象有效,而@throw也可以用在其他对象上。

        3.2异常内存管理

          如果代码中有异常,内存管理执行起来会比较复杂。例如:

         - (void)mySimpleMethod

{

NSDictiongary *dictiongary = [[NSDictionary alloc] initWith..];

[self processDictiongary:dictionary];

[dictionary release];

}

          现在假设processDictionary抛出了一个异常。程序从这个方法中跳出并寻找异常处理代码。由于现在方法已经退出来了,所以字典对象并没有被释放,于是就会出现内训泄露。

          一种简单的解决办法就是使用@try 和@finally代码块,因为@finally总是会执行的。

     - (void) mySimpleMethod

    NSDictionary *dictionary = [[NSDictionary alloc] initWith...];

     @try{

          [self processDictionary:dictionary];

}

@finally{

[dictionary release];

}

      

0 0