iOS---自动释放池

原文:http://www.2cto.com/kf/201503/385489.html


自动释放池

  • 在 mrc 的代码中,没有 weak,只有 assign
  • assign 修饰符号,对对象不做任何操作,只是简单的记录地址
  • weak 是 ARC 专有的,如果对象没有其他任何对象做强引用,会被立即释放!
  • weak 的效率非常差!
  • assign 会记录住地址,对象释放后,地址仍然保留,在 MRC 开发中,野指针错误非常频繁
  • weak 安全性很好!一旦没有强引用,自动将地址设置为 nil,OC中可以向 nil 发送任何消息都不会抱错!

  • autorelease 作用就是延迟释放,给对象添加延迟释放的标记

    自动释放对象 atuorelease

    自动释放对象
    • 所有 "autorelease" 的对象,在出了作用域之后,会被自动添加到"最近创建的"自动释放池中!
    • 在自动释放池被销毁或者耗尽的时候,会向池中所有对象发送 release 消息,释放池中对象
    • 自动释放池,在 ARC & MRC 程序中,同样有效!

      MRC 下系统变量的测试

      声明变量

      ?
      1
      2
      <code> @property (nonatomic, assign) UIButton *btn;
      </code>

      向控制器View中添加按钮:

      ?
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      <code> // alloc / init - 应该出了作用域才会被释放
           //
           self.btn = [[UIButton alloc] init];
           self.btn.frame = CGRectMake( 40 , 40 , 100 , 100 );
           self.btn.backgroundColor = [UIColor orangeColor];
       
           [self.view addSubview:self.btn];
           NSLog(@ "%@" , self.view.subviews);
       
           // 类方法创建的 - 是 ‘autorelease’ 的
           // 出了作用域,会被添加到自动释放池中
           // 自动释放池被销毁前,才释放对象!
           self.btn = [UIButton buttonWithType:UIButtonTypeContactAdd];
           [self.view addSubview:self.btn];
           NSLog(@ "%@" , self.view.subviews);
       
           // ARC 上述演练不好使!以 weak 为主!
      </code>

      运行结果:

      ?
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      <code> 2015 - 03 - 25 10 : 31 : 20.055 09 -自动释放池[ 27279 : 718327 ] (
           "<_UILayoutGuide: 0x7fc5d0d346b0; frame = (0 0; 0 0); hidden = YES; layer = <calayer: 0x7fc5d0d2d810=" ">>" ,
           "<_UILayoutGuide: 0x7fc5d0e77950; frame = (0 0; 0 0); hidden = YES; layer = <calayer: 0x7fc5d0e77790=" ">>" ,
           "<uibutton: 0x7fc5d0e07910=" " 100=" " frame=" ( 40 " layer=" <CALayer: " opaque=" NO; ">>"
      )
      2015 - 03 - 25 10 : 31 : 20.063 09 -自动释放池[ 27279 : 718327 ] (
           "<_UILayoutGuide: 0x7fc5d0d346b0; frame = (0 0; 0 0); hidden = YES; layer = <calayer: 0x7fc5d0d2d810=" ">>" ,
           "<_UILayoutGuide: 0x7fc5d0e77950; frame = (0 0; 0 0); hidden = YES; layer = <calayer: 0x7fc5d0e77790=" ">>" ,
           "<uibutton: 0x7fc5d0e07910=" " 100=" " frame=" ( 40 " layer=" <CALayer: " opaque=" NO; ">>" ,
           "<uibutton: 0x7fc5d0f2f830=" " 22=" " frame=" ( 0 " layer=" <CALayer: " opaque=" NO; ">>"
      )
      </uibutton:></uibutton:></calayer:></calayer:></uibutton:></calayer:></calayer:></code>

      可以看到,无论是alloc还是buttonWithType: 都是可以添加到控制器的View上的,也就意味着,创建出来的button并没有立刻销毁

      • assign 声明的变量,在alloc后,对象并不会立刻释放,除了作用域才会销毁,即使是对象销毁后,assign记录的地址并不会清除,这就很容易造成野指针错误
      • 利用buttonWithType: 创建出来的,也不会立刻释放,系统类方法创建出来的对象,是默认autorealease的,出了作用域,会自定添加到自动释放池中,自动释放池销毁前,才会释放对象,同样assign声明的变量会持有该对象的地址,一直不肯放~

        MRC下自定义对象的自动释放

        Person.h

        ?
        1
        2
        3
        4
        5
        6
        7
        <code>
        @interface Person : NSObject
         
        + (instancetype)person;
         
        @end
        </code>

        Person.m

        • 注意:这里没有添加autorelease
          ?
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          <code> @implementation Person
           
          + (instancetype)person {
               // autorelease 作用就是延迟释放
               // 给对象添加延迟释放的标记
               Person *p = [[Person alloc] init] ;
           
               return p;
          }
           
          - ( void )dealloc {
               NSLog(@ "我去了" );
               [ super dealloc];
          }
           
          @end
          </code>

          声明Persion变量:

          ?
          1
          2
          <code> @property (nonatomic, assign) Person *p;
          </code>

          测试Demo:

          ?
          1
          2
          3
          4
          5
          6
          7
          8
          <code>- ( void )viewDidLoad {
               [ super viewDidLoad];
               // Do any additional setup after loading the view, typically from a nib.
               self.p = [Person person];
           
               NSLog(@ "%@" , self.p);
          }
          </code>

          运行结果:

          ?
          1
          2
          <code> 2015 - 03 - 25 10 : 44 : 16.652 10 -ARC自动释放[ 27546 : 726446 ] <person: 0x7fb74b45c770 = "" >
          </person:></code>

          小结:

          在MRC下,如果提供的类方法没有autorelease,那么在调用的时候,用assign变量接收,仍然会打印出对象的地址,当然对象并没有销毁(dealloc没有输出)。

          将类方法带上 autorelease
          ?
          1
          2
          <code>Person *p = [[[Person alloc] init] autorelease];
          </code>

          打印结果:

          ?
          1
          2
          3
          <code> 2015 - 03 - 25 10 : 56 : 04.224 10 -ARC自动释放[ 27690 : 732059 ] <person: 0x7f8c48f23d90 = "" >
          2015 - 03 - 25 10 : 56 : 04.238 10 -ARC自动释放[ 27690 : 732059 ] 我去了
          </person:></code>

          可以看到对象被释放了,说明autorelease给对象添加了延迟释放的标记,让对象在自动是释放池销毁,或者出了离它最近的自动释放池作用域的时候,自动释放。

          ARC中的自动释放

          变量的声明:

          ?
          1
          2
          3
          4
          <code> @property (nonatomic, weak) Person *p;
           
          @property (nonatomic, weak) UIButton *button;
          </code>

          测试Demo:

          ?
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          <code> // ARC 中,如果给 weak 做 alloc / init 的设置数值,Xcode 会提示!
               self.p = [[Person alloc] init];
           
               // 对于 Xcode 编译器而言,只要是类方法,就不会提示!
               // 如果使用 MRC 开发,所有 返回 instancetype 的类方法,返回的对象都是 autorelease 的!
               // 因为没有强引用,会被立即释放
               self.p = [Person person];
               // ARC中之所以为 null,是因为 weak,跟autorelease没有关系!
               NSLog(@ "%@" , self.p);
           
               // 之所以会这样是因为苹果框架的底层是用 MRC 写的,buttonWithType返回的对象是带有 autorelease
               self.button = [UIButton buttonWithType:UIButtonTypeContactAdd];
               NSLog(@ "%@" , self.button);
          </code>

          打印结果:

          ?
          1
          2
          3
          <code> 2015 - 03 - 25 11 : 00 : 58.756 10 -ARC自动释放[ 27778 : 735123 ] ( null )
          2015 - 03 - 25 11 : 00 : 58.769 10 -ARC自动释放[ 27778 : 735123 ] <uibutton: 0x7f96e97668a0 = "" 22 = "" frame= "(0" layer= "<CALayer:" opaque= "NO;" >>
          </uibutton:></code>

          小结

          • 如果普通对象的alloc,且对象是weak的,Xcode会自动提示警告,告诉你这个对象,一创建就会被销毁啦
          • 但是如果你提供了一个类方法,快速生成。即使内部写的是alloc,Xcode也不会警告你,但是你不能改变一创建就被销毁的事实。
          • 苹果自己提供的类方法创建的button,我们用weak接收,却没有销毁,为啥呢,因为苹果框架的底层是用MRC写的,buttoWithType:返回的对象都是带autorelease的,意味着会延迟销毁,仅仅是被添加到自动释放池而已,等待着出自动释放池作用域或者自动释放池销毁。而不会立刻销毁。

            一道经典面试题

            问题:以下代码是否有问题?如果有,如何解决?

            ?
            1
            2
            3
            4
            5
            6
            7
            8
            <code> for ( long i = 0 ; i < largeNumber; ++i) {
                     @autoreleasepool {
                         NSString *str = [NSString stringWithFormat:@ "hello - %ld" , i];
                         str = [str uppercaseString];
                         str = [str stringByAppendingString:@ " - world" ];
                     }
                 }
            </code>

            答案:如果 largeNumber 非常大,会创建太多自动释放的对象,有可能会把自动释放池"撑满" 提示:经常一次用户交互,远远不止一个 for,在 for 的前后,会有很多的代码,但是这个 for 会占用大量的自动释放池空间

            解决方案

            引入自动释放池,网络上有两种解决方案!

            ?
            1
            2
            3
            4
            5
            6
            7
            8
            9
            <code> 1 > 外面加自动释放池(快):能够保证 for 循环结束后,内部产生的自动释放对象,都会被销毁
                 需要等到 for 结束后,才会释放内存
              2 > 里面加自动释放池(慢):能够每一次 for 都释放产生的自动释放对象!
             
              提问:那种方式效率高!速度差不多!大多数测试内部比外部外!
              如果面试中问道:要说里面的略快,“亲测”!!!
             
              提示:在日常工作中!有的时候,真的会出现代码内存飙升的情况!要知道解决办法!
            </code>

            设置largeNumner的数值:

            ?
            1
            2
            <code> long largeNumber = 5 * 1000 * 1000 ;
            </code>

            测试Demo:

            ?
            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            13
            14
            15
            <code>
             
            NSLog(@ "内部 start" );
                 CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
                 for ( long i = 0 ; i < largeNumber; ++i) {
                     @autoreleasepool {
                         NSString *str = [NSString stringWithFormat:@ "hello - %ld" , i];
                         str = [str uppercaseString];
                         str = [str stringByAppendingString:@ " - world" ];
                     }
                 }
                 NSLog(@ "end - %f" , CFAbsoluteTimeGetCurrent() - start);
             
                 [self answer1];
            </code>

            answer1方法:

            ?
            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            <code> NSLog(@ "外面 start" );
                 CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
                 @autoreleasepool {
                     for ( long i = 0 ; i < largeNumber; ++i) {
             
                         NSString *str = [NSString stringWithFormat:@ "hello - %ld" , i];
                         str = [str uppercaseString];
                         str = [str stringByAppendingString:@ " - world" ];
                     }
                 }
                 NSLog(@ "end - %f" , CFAbsoluteTimeGetCurrent() - start);
            </code>

            测试结果:

            ?
            1
            2
            3
            4
            5
            <code> 2015 - 03 - 25 11 : 18 : 50.663 12 -自动释放池的面试题[ 28015 : 743360 ] 内部 start
            2015 - 03 - 25 11 : 19 : 03.996 12 -自动释放池的面试题[ 28015 : 743360 ] end - 13.332371
            2015 - 03 - 25 11 : 19 : 03.996 12 -自动释放池的面试题[ 28015 : 743360 ] 外面 start
            2015 - 03 - 25 11 : 19 : 18.227 12 -自动释放池的面试题[ 28015 : 743360 ] end - 14.230752
            </code>

            由测试结果可以知道,对于500w次循环,内部、外部添加autorelease,速度其实差不多,内部略微快一点,但这是取决于编译器。 但是,在外部添加autorelease会占用极大的内存(注意,这里跟不加autorelease不一样,不加autorelease开辟的内存是不会释放的)。 开发中,通常在内部添加。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值