autoreleasePool

添加对象在autoreleasePool中,当出了其作用域,对象就会被释放一次,如果添加在系统自带的autoreleasepool当中的时候,释放的时机不一定,

如果在viewDidLoad里面添加的对象,那么对象会在viewDIdAppear和ViewwillAppear之间释放



具体检测什么时候释放的方法如下:

1.通过lldb命令watchpoint set v 变量名设置观察点,观察变量的变化

 2.打印堆栈的信息 就会看到autoreleasePool调用了,那么对象其实就是被释放了.


AutoreleasePoolPage

autoreleasePool是没有单独的内存结构的,它是通过以AutoreleasePoolPage为结点的双向链表来实现的。


1.每一个线程的autoreleasepool其实就是一个指针堆栈。

2.每一个指针代表一个需要release的对象或者POOL_SENTINEL()的内存地址。当这个Pool被pop的时候,所有的内存地址

在这个堆栈被划分成了一个一page为结点的双向链表。pages会在必要的时候动态地增加或删除。

3.Thread-local storage(线程局部存储)指向hot page,即最新添加的autoreleased对象所在的那个page。

Autorelease Pool Blocks看到这里还需要往下看

每一个autoreleasepool只对应一个线程。



以下三种情况需要手动添加autoreleasepool:

1)如果你编写的程序不是基于UI框架的,比如说命令行工具

2)如果你编写的循环中创建了大量的临时对象

3)如果你创建了一个辅助线程


push 操作插入的是一个 POOL_SENTINEL ,而 autorelease 操作插入的是一个具体的 autoreleased 对象

自己理解的总结:

1.autoreleasepool里面的对象会在autoreleasepool释放的时候释放。

2.如果是添加在系统自带的autoreleasepool里面,viewDidLoad里面创建的对象会在viewDIdAppear之前释放,也就是pool释放了(所以不建议使用autorelease);

3.子线程里面的autorelease对象会在线程结束的时候,被释放掉。

所以auorelease最好只用在autoreleasepool里面.

每一个runloop系统会隐式的创建一个autoreleasepool,runloop结束的时候,pool drain,autorelease的对象也就会跟着释放了。

runloop包括:一个UI事件,Timer call,delegate call,分线程都会是一个新的Runloop。












1、autorelease是什么?

      autorelease是一种支持引用计数的内存管理方式。

      它可以暂时的保存某个对象(object),然后在内存池自己的排干(drain)的时候对其中的每个对象发送release消息
      注意,这里只是发送release消息,如果当时的引用计数(reference-counted)依然不为0,则该对象依然不会被释放。可以用该方法来保存某个对象,也要注意保存之后要释放该对象。

      autorelease可以通过NSAutoreleasePool创建实例。 

2、为什么会有autorelease?


      OC的内存管理机制中比较重要的一条规律是:谁申请,谁释放。

      考虑这种情况,如果一个方法需要返回一个新建的对象,该对象何时释放?


       方法内部是不会写release来释放对象的,因为这样做会将对象立即释放而返回一个空对象;调用者也不会主动释放该对象的,因为调用者遵循“谁申请,谁释放”的原则。那么这个时候,就发生了内存泄露。

      针对这种情况,Objective-C的设计了autorelease,既能确保对象能正确释放,又能返回有效的对象。

      在autorelease的模式下,下述方法是合理的,即可以正确返回结果,也不会造成内存泄露。

  1. ClassA *Func1()
  2. {
  3. ClassA *obj = [[[ClassA alloc]init]autorelease];
  4. return obj;
  5. }
复制代码

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方法测试你的应用是否是多线程模式


详细可以参考官方API的NSAutoreleasePool Class Reference


有关autorelesePool的使用:

不要把大量循环操作放到同一个NSAutoreleasePool之间,道理同上,这样会使池中有大量对象,导致程序在运行时占用较多内存。比如下面这段代码:

int  main ( int  argc, const  char  * argv[]) { 
     
     NSAutoreleasePool  * pool = [[ NSAutoreleasePool  alloc] init];   
         
     int  i;
     
     for  (i=0;i<10000;i++){
         Sample *s = [Sample new ];
         s.flag = i;
         NSLog (@ "%@" ,[s toString]);
         [s autorelease];
     }
    
     [pool release];
     return  0;  
     
}

上面的代码运行时,必须等整个循环结束后才开始销毁对象。可以改进为下面这样:

int  main ( int  argc, const  char  * argv[]) { 
     
     NSAutoreleasePool  * pool = [[ NSAutoreleasePool  alloc] init];   
         
     int  i;
     
     for  (i=0;i<10000;i++){
         Sample *s = [Sample new ];
         s.flag = i;
         NSLog (@ "%@" ,[s toString]);
         [s autorelease];
         if  (i % 100==0)
         {
             [pool release];
             pool = [[ NSAutoreleasePool  alloc] init];
         }
     }
    
     [pool release];
     return  0;  
     
}

这样每当池子里有100个对象时,就释放一次,这样程序在运行时占用的内存就会少很多


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值