• iOS---NSAutoreleasePool自动释放原理及详解


    前言:当您向一个对象发送一个autorelease消息时,Cocoa就会将该对象的一个引用放入到最新的自动释放池。它仍然是个正当的对象,因此自动释放池 定义的作用域内的其它对象可以向它发送消息。当程序执行到作用域结束的位置时,自动释放池就会被释放,池中的所有对象也就被释放。

    1. ojc-c 是通过一种"referring counting"(引用计数)的方式来管理内存的, 对象在开始分配内存(alloc)的时候引用计数为一,以后每当碰到有alloc,new,copy,retain的时候引用计数都会加一, 每当碰到release和autorelease的时候引用计数就会减一,如果此对象的计数变为了0, 就会被系统销毁.
    2. NSAutoreleasePool 就是用来做引用计数的管理工作的,这个部分后面会详细说到.
    3. autorelease和release没什么区别,只是引用计数减一的时机不同而已,autorelease会在对象的使用真正结束的时候才做引用计数减一.
    4.设定项目编译环境为ARC下时,编译器会帮助我们在程序的入口main函数就调用NSAutoreleasePool,这样保证程序中不调用NSAutoreleasePool,但在退出时自动释放

    1.NSAutoreleasePool是什么?
     NSAutoreleasePool实际上是个对象引用计数自动处理器,在官方文档中被称为是一个类。

    NSAutoreleasePool可以同时有多个,它的组织是个栈,总是存在一 个栈顶pool,也就是当前pool,每创建一个pool,就往栈里压一个,改变当前pool为新建的pool,然后,每次给pool发送drain消 息,就弹出栈顶的pool,改当前pool为栈里的下一个 pool。

    2.NSAutoreleasePool可以用来做什么,怎么用?

    NSAutoreleasePool可以在一定程度上帮助我们苹果开发程序员管理内存,让我们的工作更加严密,简便。

      1)在ARC项目中,系统会自动帮助我们在程序中嵌入NSAutoreleasePool,此为苹果公司程序员在写这个编译器的时候设定的;

      2)在MRC项目中,我们需要自己去创建NSAutoreleasePool类对象去帮助我们管理内存;

      3)使用应注意:

          a.在ARC项目中我们同样可以创建NSAutoreleasePool类对象去帮助我们更精确的管理内存问题。

          b. NSAutoreleasePool的管理范围是在NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];与[pool release];之间的对象

          c..既然ARC项目中设置了ARC,为什么还要使用@autoreleasepool?(注意a的案例解释)

              ARC 并不是舍弃了 @autoreleasepool,而是在编译阶段帮你插入必要的 retain/release/autorelease 的代码调用。

              所以,跟你想象的不一样,ARC 之下依然是延时释放的,依然是依赖于 NSAutoreleasePool,跟非 ARC 模式下手动调用那些函数本质上毫无差别,只是编译          器来做会保证引用计数的正确性。

              参考:      Retain count semantics in ARC

                       What's @autoreleasepool

      • d.NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
        当执行[pool autorelease]的时候,系统会进行一次内存释放,把autorelease的对象释放掉,如果没有NSAutoreleasePool , 那这些内存不会释放
        注意,对象并不是自动被加入到当前pool中,而是需要对对象发送autorelease消息,这样,对象就被加到当前pool的管理里了。当当前pool接受到drain消息时,它就简单的对它所管理的所有对象发送release消息。(如例子1)
      • e.在ARC项目中.不能直接使用autorelease pools,而是使用@autoreleasepool{},
        @autoreleasepool{}比直接使用NSAutoreleasePool效率高。不使用ARC的时候也可以使用(autorelease嵌套

      4)使用例子:

    例子1:

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    NSString* nsstring;
    char* cstring = "Hello CString";
    nsstring = [NSString stringWithUTF8String:cstring];
    [pool release];(这一行代码就是在给pool发送drain消息了)

     









     

    官方API摘抄翻译:

    3.autorelease的原理是什么?

           Autorelease实际上只是把对release的调用延迟了,对于每一个Autorelease,系统只是把该Object放入了当 前的Autorelease pool中,当该pool被释放时,该pool中的所有Object会被调用Release。

    4.!!!autorelease何时释放?

           对于autorelease pool本身,会在如下两个条件发生时候被释放(详细信息请参见第5条)

    1)、手动释放Autorelease pool


    2)、Runloop结束后自动释放

           对于autorelease pool内部的对象在引用计数的retain == 0的时候释放。release和autorelease pool 的 drain都会触发retain--事件。

    5、autorelease释放的具体原理是什么?


           要搞懂具体原理,则要先要搞清楚autorelease何时会创建。

          
    我们的程序在main()调用的时候会自动调用一个autorelease,然后在每一个 Runloop, 系统会隐式创建一个Autorelease pool,这样所有的release pool会构成一个象CallStack一样的一个栈式结构,在每一个Runloop结束时,当前栈顶的 Autorelease pool(main()里的autorelease)会被销毁,这样这个pool里的每个Object会被release。

          
    可以把autorelease pool理解成一个类似父类与子类的关系,main()创建了父类,每个Runloop自动生成的或者开发者自定义的autorelease pool都会成为该父类的子类。当父类被释放的时候,没有被释放的子类也会被释放,这样所有子类中的对象也会收到release消息。

          
    那什么是一个Runloop呢? 一个UI事件,Timer call, delegate call, 一个鼠标事件,键盘按下(MAC OSX),或者iphone上的触摸事件,异步http连接下后当接收完数据时,都会是一个新的Runloop。

          
    一般来说,消息循环运行一次是毫秒级甚至微秒级的,因此autorelease的效率仍然是非常高的,确实是一个巧妙的设计。

    6、使用有什么要注意的?


    1)、NSAutoreleasePool可以创建一个autorelease pool,但该对象本身也需要被释放,如:

    1. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init;
    2. // Code benefitting from a local autorelease pool.
    3. [pool release];
    复制代码


           在引用计数环境下,使用[pool release]或[pool drain]效果是相同的,drain仅适用于max os高版本,低版本不适用,而release通用,其它并无太大差别。

    2)、在ARC下,不能使用上述方式调用autorelease,而应当使用@autoreleasepool,如:

    1. @autoreleasepool {
    2.    // Code benefitting from a local autorelease pool.
    3. }
    复制代码

    3)、尽量避免对大内存使用该方法,如图片。对于这种延迟释放机制,还是尽量少用,最好只用在方法内返回小块内存申请地址值的情况下,且参考和领会OC的一些系统方法,如:[NSString stringWithFormat:]。

    4)、不要把大量循环操作放到同一个NSAutoreleasePool之间,这样会造成内存峰值的上升。

    7、关于多线程,有什么要注意的?

            我还未实际使用到,在官方API翻译出类似如下语句:

    1)、对于不同线程,应当创建自己的autorelease pool。如果应用长期存在,应该定期drain和创建新的autorelease pool

           下面这句话摘自官方API,大概是说多线程中如果没有使用到cocoa的相关调用,则不需要创建autorelease pool,我一直没有理解透彻

    If, however, your detached thread does not make Cocoa calls, you do not need to create an autorelease pool.


    2)、如果不是使用的NSThread,就不要用aoturelease pool,除非你是多线程模式(multithreading mode) ,可以使用NSThread的isMultiThreaded方法测试你的应用是否是多线程模式

    PS:

    我把它理解为:新开线程最好实现NSAutoreleasePool(当 我们点击一个App中的一个按钮或者其他可以触碰开启新业务的UI控件,在程序里面就会自动开启一条新线程,当我们不用这个业务的时候,就需要程序帮我们 提前在“程序的主窗口的所有代码编译结束后”之前关闭这条线程以优化线程的使用,一定程度上尽量避免线程开启太多占用CPU严重引起的卡顿问题)(业务逻 辑处理-业务线程优化)
    详细可以参考官方API的NSAutoreleasePool Class Reference

  • 相关阅读:
    如何保存一张网页上的图片(C#)到本地计算机上
    SQL Server2005常用基本管理操作
    C#保存图片到数据库,读取图片显示
    简析正则表达式
    HDU 变形课
    HDU 1272 小希的迷宫
    HDU 1856 More is better
    POJ 1269 Intersecting Lines
    HDU Football Score
    HDU 1213 How Many Tables
  • 原文地址:https://www.cnblogs.com/LifeTechnologySupporter/p/5232357.html
Copyright © 2020-2023  润新知