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

第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条)