美文网首页
编写高质量iOS与OS X代码的52个有效方法--第34条

编写高质量iOS与OS X代码的52个有效方法--第34条

作者: 8fe8946fa366 | 来源:发表于2018-04-13 16:36 被阅读7次

    第34条:以“自动释放池”降低内存峰值

    1.在哪写自动释放池

    自动释放池往往是不需要我们自己写的,每一个线程都有默认的自动释放池,每次执行一次runloop就会自动把自动释放池清空。

    通常只有一个地方需要自己写自动释放池块,就是main函数

    int main(int argc, char * argv[]) {

        @autoreleasepool {

            return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));

        }

    }

    这个自动释放池用来捕捉UIApplicationMain函数自动释放的对象,可以理解成最外围捕捉全部自动释放对象所用的池。

    2.自动释放池的释放时机

    ⚠️:这个问题非常重要,我们也已经多次研究了,一定要记住。

    在没有手加Autorelease Pool(@autoreleasepool{})的情况下,Autorelease对象是在当前的runloop迭代结束时释放的,而它能够释放的原因是系统在每个runloop迭代中都加入了自动释放池Push和Pop

    在每一个runloop的迭代过程中,在runloop里会注册两个observer,第一个observer监测即将进入runloop状态(kCFRunLoopEntry),并且调用_objc_autoreleasePoolPush()方法,创建一个自动释放池,这个方法的优先级最高,确保创建自动释放池的操作在其他所有操作之前执行。

    第二个observer监测runloop的两个状态,一个是BeforeWaiting。在监测到这个状态以后调用_objc_autoreleasePoolPop()方法,销毁当前的自动释放池。并且调用_objc_autoreleasePoolPush()方法,创建新的自动释放池。另一个是exit,即将退出runloop这个状态。这个时候调用_objc_autoreleasePoolPush()方法,销毁自动释放池。

    所以说,不是手写的自动释放池的生命周期是和他所在线程的runloop息息相关的,runloop迭代结束时才会销毁这个自动释放池,同时向其中的对象发送release消息。

    3.利用自动释放池解决内存峰值问题

    正是因为了解了自动释放池的释放时机,才产生了内存峰值问题。

    比如说我们在一个for循环中创建了大量的局部变量:

    for(int i = 0;i<10000;i++){

    [self doSomethingWithInt: i];

    }这个时候所有在for循环中的变量都被添加到自动释放池,但是这个时候runloop没有迭代结束,那么for循环中的这些变量始终没办法释放,内存就会持续上涨,如果这些对象占据很大的内存,那么很快程序就会发出内存警告了。

    那么这个时候就可以在for循环里加一个自动释放池,把生成变量的代码包裹住,每次循环都会释放。

    for(NSDictionary* record in databaseRecord){

    @autoreleasepool{

    Person* person = [[Person alloc] initWithRecord:record];

    xxx;

    }

    }

    相关文章

      网友评论

          本文标题:编写高质量iOS与OS X代码的52个有效方法--第34条

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