对于autoreleasePool的一些思考

以下考虑均基于ARC

顾名思义 autoreleasePool 是一种自动的内存管理机制,它的工作机制很简单,就是把需要自动管理的对象放到 autoreleasePool 中,待 autoreleasePool 结束后把池子中的对象释放掉。

首先补充说明一点,在OC中 自生成并持有对象的方式只有 alloc/new/copy/mutableCopy 四种 ,其他方式均为非自生成并持有对象 如下代码

    {
        //自生成并持有对象
        NSMutableArray *array1 = [[NSMutableArray alloc]init];
        
        //非自生成并持有对象, 因为array2 持有的是通过 +()array 方法返回的对象
        NSMutableArray *array2 = [NSMutableArray array];
    }

接着autoreleasePool说,正常情况下,在超出作用域时对象会被自动释放掉,如下代码

    {
        NSObject *obj = [[NSObject alloc]init];
        //自己生成并持有对象,引用计数 + 1 (retain)
        //retainCount = 1
    }
    //超出作用域 obj 引用计数 -1 (release)
    //此时retainCount = 0 所以 obj 销毁

retain 和 release 成对出现,不需要另加一个自动释放池来进行管理,一样能完成内存的自动回收。这不就扯了吗?本来就好好的东西,干嘛非得加个自动释放池呢?看下面的一个例子

- (NSObject *)getObj {
    NSObject *obj = [[NSObject alloc]init];
    //自己生成并持有对象,引用计数 + 1 (retain)
    //retainCount = 1
    return obj;
    //return 导致提前出作用域 引用计数 -1 (release)
    //retainCount = 0 obj 被释放
}

当我们需要调用这个函数赋值时 如下

    {
        NSObject *obj_1 = [self getObj];
    }

问题来了,正如刚才所说,正常情况下 出作用域时对象会被自动释放掉,于是就造成了 obj_1 在想取得持有对象时 发现对象被释放掉了,这显然是不合理的。这就像是你满心欢喜在天猫买了个冰棒,拿到快递时发现冰棒竟然化没了,你说闹心不闹心。

虽然道理是这个道理,但在实际工作时并没有这种情况发生,这是怎么回事呢?这其实就是autoreleasePool的功劳了,编译器会在return 之前提前把对象retain 并 注册到自动释放池 大体过程类似下面的代码(这里只是用于演示过程)

- (NSObject *)getObj {
    NSObject *obj = [[NSObject alloc]init];
    //自己生成并持有对象,引用计数 + 1 (retain)
    //retainCount = 1
    
    //下面这一步由编译器自动完成
    NSObject *autoreleaseObj = obj;
    [autoreleaseObj retain];
    //obj 引用计数 + 1
    //retainCount = 2
    
    [autoreleaseObj autorelease];
    //把autoreleaseObj注册到自动释放池
    
    return autoreleaseObj;
    //return 导致提前出作用域 引用计数 -1 (release)
    //retainCount = 1 obj不会被释放
    //此时obj 被autoreleasePool持有
}

那这个pool在哪里呢?对于oc来说,整个程序都是运行在一个pool中的,可以看一下main函数的实现

int main(int argc, char * argv[]) {
    //自动释放池
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

此时由于return后对象被autoreleasePool持有,因此不会被提前释放掉,obj_1 自然也就可以拿到并持有该对象引用计数+1。这样,当出作用域时obj_1被释放,引用计数-1,当autoreleasePool退出时 对象引用计数 -1 至此被注册到autoreleasePool的对象的引用计数 = 0 被释放掉。由此完成了内存的自动管理。

问题是解决了,但会造成内存消耗增加,这就好比天猫的员工站出来说,为了保障你的冰棒半途不会化掉,你需要多加点钱我们给你配一个保温箱。

为什么会增加内存消耗?由于对象会被注册到自动释放池,而且在自动释放池结束前对象一直被持有,因此当大量的对象被注册到自动释放池时就会造成内存激增,看下面一段代码

  - (void)viewDidLoad {
    [super viewDidLoad];

    for (int i = 0; i < 10000; i ++) {
        for (int j = 0; j < 100; j ++) {
            NSMutableArray *array = [self getArray];
            NSLog(@"%@",array);
        }
    }
 }

- (NSMutableArray *)getArray {
    NSMutableArray *arr = [[NSMutableArray alloc]init];
    [arr addObject:@"hh"];
    return arr;
}

事实上,只要是调用非自己生成并持有的对象,该对象就会被注册到自动释放池,比如下面的方式

NSMutableArray *arr = [NSMutableArray arrayWithObjects:@"hh", nil];
//由于调用的是非自己生成并持有对象,所以会被注册到自动释放池,因此在循环调用时一样会造成内存激增。需要注意

接着内存暴增说,既然自动释放池会造成内存暴增,那肯定要找方法来解决,我们从自动释放池的释放过程入手来解决这个问题,首先说一下自动释放池的嵌套。

关于嵌套,这里补充一点,当多层自动释放池嵌套时,内层自动释放池会屏蔽掉外层自动释放池对内层自动释放池中的对象retain,很绕,说的直白一点,就是内层自动释放池中的对象只会注册到内层自动释放池中。如下

//外层池子
    @autoreleasepool {
        //内层池子
        @autoreleasepool {
            NSMutableString *poolStr = [NSMutableString stringWithString:@"hh"];
            //非自己生成并持有对象,poolStr被注册到内层池子
        }
        //内层池子结束,poolStr释放
    }

结合上面自动释放池内存激增的原理,既然内层池子释放时,注册到内层池子的对象也会被释放,因此对于内存激增的问题,我们可以采用自动释放池的嵌套来解决,对于上面的代码我们稍加修改,如下

 - (void)viewDidLoad {
    [super viewDidLoad];

    for (int i = 0; i < 10000; i ++) {
        //内层池子
        @autoreleasepool {
            for (int j = 0; j < 100; j ++) {
                NSMutableArray *array = [self getArray];
                NSLog(@"%@",array);
            }
        }
    }
 }

- (NSMutableArray *)getArray {
    NSMutableArray *arr = [[NSMutableArray alloc]init];
    [arr addObject:@"hh"];
    return arr;
}

此时,由于内层池子中的一百次循环完毕后,内层池子便会结束,因此注册到内层池子的对象便会被即时释放,因此内存不会继续增加。

额外补充:其实当开辟一条新的线程时,同时也会创建一个pool用于新线程的内存管理,但是POSIX由于不在ARC的内存管理范围之内,因此通过pthread创建的新线程需要自己创建atoreleasePool,这一点可以通过打印autoreleasePool得知(打印方法,自行百度),由于class cluster 和 tagged Pointer优化 会造成部分对象的表现异常,比如NSString 和 NSNumber在内容较少时并不会生成真正的对象,因此也不会被加入到自动释放池。

关于autoreleasePool就先整理到这吧,有不对的地方还请大神们不吝赐教。

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

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,036评论 1 32
  • 局部释放池 创建一个新的自动释放池的方法:ARC下: 这相当于MRC下: 其中对象s会被加入到自动释放池,当ARC...
    thinkq阅读 16,597评论 8 40
  • 1、[NSObject alloc]在创建完对象后,会让该对象的retainCount+1,后续的init为初始化...
    naiyi阅读 1,461评论 0 4
  • 29.理解引用计数 Objective-C语言使用引用计数来管理内存,也就是说,每个对象都有个可以递增或递减的计数...
    Code_Ninja阅读 1,421评论 1 3
  • 面试题参考1 : 面试题[http://www.cocoachina.com/ios/20150803/12872...
    江河_ios阅读 1,628评论 0 4