Autorelease Pool学习笔记

参考

自动释放池的前世今生 ---- 深入解析 Autoreleasepool

你真的懂iOS的autorelease吗?

@autoreleasepool-内存的分配与释放

Autorelease Pool是什么

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

新建一个工程,总会见到这样的几行代码,这行代码将所有的事件、消息全部交给了UIApplication来处理。

同时也说明,整个iOS应用,是包含在一个自动释放池block中的。

编译的时候,这段代码会被转换成

{
    __AtAutoreleasePool __autoreleasepool;
}

其中,出现的结构体__AtAutoreleasePool

struct __AtAutoreleasePool {
  __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
  ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
  void * atautoreleasepoolobj;
};

这表明,我们的main函数实际执行了

int main(int argc, const char * argv[]) {
    {
        void * atautoreleasepoolobj = objc_autoreleasePoolPush();
        // do whatever you want
        objc_autoreleasePoolPop(atautoreleasepoolobj);
    }
    return 0;
}

结论 @autoreleasepool 只是帮助我们少写了这两行代码而已,让代码看起来更美观,然后要根据上述两个方法来分析自动释放池的实现。

Autorelease Pool的主要结构

每一个autorelease pool都是由一系列的AutoreleasePoolPage组成

class AutoreleasePoolPage {
    magic_t const magic;                //完整性检验
    id *next;
    pthread_t const thread;             //保存当前的进程
    AutoreleasePoolPage * const parent; //双线链表使用 指向父节点
    AutoreleasePoolPage *child;         //双向链表使用 指向子结点
    uint32_t const depth;
    uint32_t hiwat;
};

可见,自动释放池的AutoreleasePoolPage是以双向链表的结构连接起来的

而在自动释放池的内存中,AutoreleasePoolPage被以栈结构存储起来,如下图

AutoreleasePoolPage在内存中的结构

更多详细实现,请移步自动释放池的前世今生 ---- 深入解析 Autoreleasepool

MRC与ARC时代的Autorelease

MRC(Mannul Reference Counting)和ARC(Automatic Reference Counting),分别对应着手动引用计数和自动引用计数。

对!是计数,不是“ GC、垃圾回收 ”什么的,就是说,在Objective-C的开发中,ARC不代表像Java那样有GC做垃圾回收,所以本质上还是要“手动”管理内存的。也就是说,我们在ARC环境下写的代码,不用自己手动插入“ retainrelease这些消息 ”,ARC会在编译时为我们在合适的位置插入,释放不必要的内存。

@autoreleasepool 就跟对象的release密切相关。

MRC时代,如果我们想先retain一个对象,但是并不知道在什么时候可以release它,我们可以像下面这么做:

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString* str = [[[NSString alloc] initWithString:@"tutuge"] autorelease];
//use str...
[pool release];
//str is released

就是说,我们可以在创建对象的时候给对象发送“ autorelease”消息,然后当 NSAutoreleasePool结束的时候,“标记过”autorelease的对象都会被“ release”掉,也就是会被释放掉。

但是在ARC时代,我们不用手动发送autorelease消息,ARC会自动帮我们加。而这个时候,@autoreleasepool做的事情,跟 NSAutoreleasePool就一模一样了。

Autorelease对象的释放时机

实践内容可以参考 你真的懂iOS的autorelease吗?

在上面引用的文章中,笔者用__weak id指针指向一个autorelease对象,在不增加引用的情况下观察autorelease对象的释放情况。

  • 首先在viewDidLoad方法中初始化一个Array对象
  • 分别尝试在viewWillAppearviewDidAppeare方法中观测Array的值

发现,在viewWillAppear方法中,对象未被释放,而到了viewDidAppear中就被释放了,发现Array对象并非超出作用域就马上被释放。得出结论,autorelease并不是根据对象的作用域来决定释放时机。

实际上,autorelease释放对象的依据是Runloop,简单说,runloop就是iOS中的消息循环机制,当一个runloop结束时系统才会一次性清理掉被autorelease处理过的对象,其实本质上说是在本次runloop迭代结束时清理掉被本次迭代期间被放到autorelease pool中的对象的。至于何时runloop结束并没有固定的duration。

扩展

既然由runloop来决定对象释放时机而不是作用域,那么,在一个{}内使用循环大量创建对象就有可能带来内存上的问题,大量对象会被创建而没有及时释放,这时候就需要靠我们人工的干预autorelease的释放了。

上文有提到autorelease pool,一旦一个对象被autorelease,则该对象会被放到iOS的一个池:autorelease pool,其实这个pool本质上是一个stack,扔到pool中的对象等价于入栈。我们把需要及时释放掉的代码块放入我们生成的autorelease pool中,结束后清空这个自定义的pool,主动地让pool清空掉,从而达到及时释放内存的目的。以上述图片处理的例子为例,优化如下:

for (int i = 0; i <= 1000; i ++) {
    //创建一个自动释放池
    NSAutoreleasePool *pool = [NSAutoreleasePool new];//也可以使用@autoreleasePool{domeSomething}的方式
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"PNG"];
    UIImage *image = [[UIImage alloc] initWithContentsOfFile:filePath];
    UIImage *image2 = [image imageByScalingAndCroppingForSize:CGSizeMake(480, 320)];
    [image release];
    //将自动释放池内存释放,它会同时释放掉上面代码中产生的临时变量image2
    [pool drain];
}

这样,每次循环结束时,可以及时的释放临时对象的内存,其中,对自动释放池的操作可以用上文提到的方法来替代。

@autoreleasePool{
    //domeSomeThing;
}

什么时候用@autoreleasepool

根据 Apple的文档 ,使用场景如下:

  • 写基于命令行的的程序时,就是没有UI框架,如AppKitCocoa框架时。
  • 写循环,循环里面包含了大量临时创建的对象。(本文的例子)
  • 创建了新的线程。(非Cocoa程序创建线程时才需要)
  • 长时间在后台运行的任务。

什么对象会加入Autoreleasepool中

  1. 使用allocnewcopymutableCopy的方法进行初始化时,由系统管理对象,在适当的位置release
  2. 使用array会自动将返回值的对象注册到Autoreleasepool
  3. __weak修饰的对象,为了保证在引用时不被废弃,会注册到Autoreleasepool中。
  4. id的指针或对象的指针,在没有显示指定时会被注册到Autoleasepool中。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,333评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,812评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,016评论 0 246
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,293评论 0 214
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,650评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,788评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,003评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,741评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,462评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,681评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,168评论 1 262
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,528评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,169评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,119评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,902评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,846评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,719评论 2 274

推荐阅读更多精彩内容

  • 1.1 什么是自动引用计数 概念:在 LLVM 编译器中设置 ARC(Automaitc Reference Co...
    __silhouette阅读 4,965评论 1 17
  • 1、[NSObject alloc]在创建完对象后,会让该对象的retainCount+1,后续的init为初始化...
    naiyi阅读 1,461评论 0 4
  • 11.看下面的程序,第一个NSLog会输出什么?这时str的retainCount是多少?第二个和第三个呢? 为什...
    AlanGe阅读 668评论 1 4
  • 内存管理 简述OC中内存管理机制。与retain配对使用的方法是dealloc还是release,为什么?需要与a...
    丶逐渐阅读 1,867评论 1 16
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,037评论 29 470