iOS内存管理——autorelease方法解析

来源:互联网 发布:中文域名续费诈骗 编辑:程序博客网 时间:2024/06/01 07:45

iOS内存管理——autorelease

  • autorelease 自动释放
  • 注意:autorelease和ARC 是完全不同的两个东西,没有任何联系。

autorelease和ARC

  • autorelease的内存管理方式是在autoreleasePool释放的时候对其中持有的对象依次进行release操作,这样做有个隐患就是如果autoreleasePool迟迟不能被释放,则会有大量的对象在内存中,会有内存峰值的问题存在。
  • ARC则是在对象不被使用的时候释放,它的管理规则我们会在后边的文章详细讨论,这里只需要知道它们是完全不同的两个东西即可。

aurorelease的工作方式

{    声明变量。。}变量释放

在c语言中我们会说局部变量超出了作用域而被释放,”{}”的范围即是它的作用域。

在autoreleasePool的工作方式中我们也有作用域的概念存在,只不过不是用”{}”来划分的,而是NSAutoreleasePool。步骤如下:

    1,生成并持有NSAutoreleasePool对象;    2,调用已分配对象的autorelease实例方法;    3,废弃NSAutoreleasePool对象;

代码如下:

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];    id obj = [[NSObject alloc] init];    [obj autorelease];    [pool drain];

autorelease发生内存峰值的情况

在我们加载大量的图片时会有这个风险,eg:

for(int i=0; i<100; i++) {    1,创建图片    2,产生大量的autorelease对象    3,在没有使用NSAutoreleasePool控制作用域的情况下没有及时的释放2中创建对象。    4,内存峰值上升}

解决方法:

for(int i=0; i<100; i++) {    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];    1,创建图片    2,产生大量的autorelease对象    //废弃pool,其中的autorelease对象会调用release方法。    [pool drain];}

autorelease对象产生峰值的原因 autorelease对象到底什么时候释放 引用自(这个博客)[http://www.jianshu.com/p/494481d59278]

主线程有个事件循环。每次有事件的时候,由cocoa框架调用我们写的代码,再回到cocoa框架。NSAutoreleasePool其实是由cocoa框架管理的,当出了我们写的代码,回到框架的时候,放入NSAutoreleasePool的对象就会由框架调用release, 这样就保证跑我们代码的时候,对象是会存在的,出了我们的代码,对象也能够正常地被释放。
比如

系统代码(事件激发)
funA()
funB()
funC()
funD()
系统代码(等待事件)

在funA, funB, funC, funD中,无论调用多少层,也是用户写的代码。autorelease的对象还是存在的。下一次事件激发的时候,上一次事件的对象是不存在的,因为已经被系统框架调用release回收了。除非你自己调用了retain, 自己调用了一次retain, 就应该自己来调用一次release, 这样也没有违背上面说的原则。
自动释放池的缺点:它延缓了对象的释放,在有大量自动释放的对象时,会占用大量内存资源。因此,你需要避免将大量对象自动释放。并且,在以下两种情况下,你需要手动建立并手动销毁掉自动释放池:
1.当你在主线程外开启其它线程时:系统只会在主线程中自动生成并销毁掉自动释放池。(runloop未开启)
2.当你在短时间内制造了大量自动释放对象时:及时地销毁有助于有效利用iPad上有限地内存资源

iOS的运行时是由一个一个runloop组成的,每个runloop都会执行下图的一些步骤:
runloop
可以看到,每个runloop中都创建一个Autorelease Pool,并在runloop的末尾进行释放,
所以,一般情况下,每个接受autorelease消息的对象,都会在下个runloop开始前被释放。也就是说,在一段同步的代码中执行过程中,生成的对象接受autorelease消息后,一般是不会在代码段执行完成前释放的。

简洁来说:

autorelease对象放在autoreleasePool中。
autoreleasePool销毁时autorelease对象才会被释放。
autoreleasePool默认是在NSRunloop的最后阶段自动被执行,也可以手动强制的调用销毁方法。
所以autorelease对象的释放时间是在NSRunLoop的最后阶段自动释放或者手动强制释放

autorelease 实现

在objc4库的runtime/objc-arr.mm中

  • objc4/runtime/objc-arr.mm class Auto autoreleasePoolPage
class AutoreleasePoolPage{    static inline void *push()    {        生成或者持有NSAutoreleasePool对象    }    static inline void *pop()    {        废弃NSAutoreleasePool对象;        releaseAll()    }    static inline id autorelease(id obj)    {        相当于往pool中添加对象        1,取得最近的pool对象        2,autoreleasePoolPage->add(obj)    }    id *add(id obj)    {        将对象追加到内部数组中    }    void releaseAll()    {        调用内部数组中对象的release实例方法    }};void *objc_autoreleasePoolPush(void){    return AutoreleasePoolPage::push();}void *objc_autoreleasePoolPop(void *ctxt){    AutoreleasePoolPage::pop(ctxt);}id *objc_autorelease(id obj) {    return AutoreleasePoolPage::autorelease(obj);}

通过如上的代码解析,我们之前看到的autoreleasePool的工作方式可以进一步解释为:

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];    //objc_autoreleasePoolPush();    id obj = [[NSObject alloc] init];    [obj autorelease];    //objc_autorelease(obj);    [pool drain];    //objc_autoreleasePoolPop(pool);

如果对NSAutoreleasePool对象使用autorelease方法会发生什么

会发生异常

通常在Foundation框架中,无论哪个实例对象调用autorelease方法都是调用NSObject类的autorelease实例方法。
但是NSAutoreleasePool类对autorelease方法进行了重载,所以会发生异常

0 0
原创粉丝点击