美文网首页
局部释放池和RunLoop释放池

局部释放池和RunLoop释放池

作者: 为什么划船不靠桨 | 来源:发表于2018-03-15 09:21 被阅读0次
局部释放池和RunLoop释放池的概念:

主线程的RunLoop是默认开启的. 每一次消息循环开始的时候会先创建自动释放池,这次循环结束前,会释放自动释放池,然后RunLoop等待下次事件源。 在这个过程中,由RunLoop创建的释放池类似于一个全局的释放池。但是开发者可以任何执行的地方创建释放池,也就是局部的释放池,这时的释放池类似于代码块 当释放池结束的时候会自动释放。因此一般情况下,局部的自动释放池很快就被释放了,而RunLoop释放池会等一次消息循环结束的时候释放。

什么样的对象会交给释放池管理:

返回当前类的实例的类方法创建出来的对象,都是autorelease的,会交给所在的释放池进行管理。 例如创建一个Person类,使用[[self alloc]init]方法创建的对象的管理不会交给它所在的释放池,而是根据引用计数来控制释放的时机, 如果使用[[[self alloc]init] autorelease]创建的对象,会交给所在的释放池管理,控制其释放的时机。

这个时候也许大家会想到一个在面试的时候经常遇到的一个面试题:当我们需要在for循环里面循环无限次创建对象,怎么避免内存的激增?

当我们使用for循环创建很多个使用autorelease方式创建的NSString对象的时候,将所有的对象的释放权都交给了RunLoop 的释放池,而RunLoop的释放池会等待这个事件处理之后才会释放,因此就会使对象无法及时释放,堆积在内存造成内存泄露,可以在Debug Navigation 中观察到内存激增。为了验证确实是因为autorelease这种创建方式引起的内存泄露,我做了如下的测试:

int largeNumber = 100000000;
- (void)correctSolution1{
    for (int i = 0; i < largeNumber; i++) {
        NSString *str = [[NSString alloc] initWithFormat:@"hello -%04d", i];
        str = [str stringByAppendingString:@" - world"];
    }
}

// stringWithFormat:本质上是调用了alloc + initWithFormat: + autorelease
// 我在本例中将stringWithFormat:方法换成了alloc + initWithFormat:
// 这样做问题就解决了:内存几乎没有变化。反向验证了内存飙升确实是autorelease创建方式造成的。
但是在编写代码的时候我们仍然习惯用类的快速创建方法,而不是alloc+init。也就是说,为了方便写程序,又使用了底层实现是alloc+init+autorelease的快速创建对象的方法(如 stringWithFormat:)。因此解决的方案就是添加局部的释放池,以及时释放内存,如果将局部释放池添加到循环外:

- (void)wrongSolution{
    @autoreleasepool {
        for (int i = 0; i < largeNumber; i++) {
            NSString *str = [NSString stringWithFormat:@"hello -%04d", i];
            str = [str stringByAppendingString:@" - world"];
        }
    }
}

这样显然是没有效果的,释放池需要等循环执行之后再释放内存,这和使用RunLoop创建的释放池没有什么区别。 较好的方案就是每次循环的时候添加一个释放池:

- (void)correctSolution2{
    for (int i = 0; i < largeNumber; i++) {
        @autoreleasepool {
            NSString *str = [NSString stringWithFormat:@"hello -%04d", i];
            str = [str stringByAppendingString:@" - world"];
        }
    }
}

这样每一次循环的结束时都会释放一次内存,因而这个循环全部执行完成时也几乎不消耗内存。

总结是:

做多线程开发时,需要在线程调度方法中手动添加自动释放池,尤其是当执行循环的时候,如果循环内部有使用类的快速创建方法创建的对象, 一定要将循环体放到自动释放池中。

相关文章

  • 局部释放池和RunLoop释放池

    局部释放池和RunLoop释放池的概念: 主线程的RunLoop是默认开启的. 每一次消息循环开始的时候会先创建自...

  • 【iOS】autoreleasepool

    每一个新的RunLoop都会隐式创建一个自动释放池。类似一个局部的垃圾回收,将部分垃圾对象集中释放。自动释放池的释...

  • 自动释放池

    首先这里先说一下RunLoop与自动释放池的关系吧 我是按照网上总结的RunLoop与自动释放池回答的面试官。当回...

  • autorelease 自动释放池的释放时机

    autorelease 自动释放池的释放时机 runloop就是iOS中的消息循环机制,当一个runloop结束时...

  • iOS 高级经典面试

    2.自动释放池什么时候释放? //第- -次创建:启动runloop时候//最后一次销毁: runloop 退出的...

  • 自动释放池 & Runloop

    前言 本篇文章会大致分析下自动释放池(AutoreleasePool)和 Runloop的底层实现原理,这两个知识...

  • 自动释放池、runloop

    自动释放池:管理对象的生命周期 magic:用来体验AutoreleasePoolPage的结构是否完整; nex...

  • Autoreleasepool

    局部释放池 创建一个新的自动释放池的方法:ARC下: 这相当于MRC下: 其中对象s会被加入到自动释放池,当ARC...

  • 自动释放池

    自动释放池的创建和释放,销毁的时机如下所示 kCFRunLoopEntry; // 进入runloop之前,创建一...

  • 内存管理(四)

    自动释放池和Runloop关系 打印如下:() 分析:重点在Runloop开头和结尾有两个 callout 明为_...

网友评论

      本文标题:局部释放池和RunLoop释放池

      本文链接:https://www.haomeiwen.com/subject/sprvuxtx.html