iOS -自动释放池相关

基本数据类型、alloc/init、new、copy初始化的对象以及isTaggedPointer对象的创建并不会引起内存的增加,这是因为自动释放池不会对这些方式创建的变量进行管理。基本数据类型的局部变量在栈区,由系统进行管理,alloc/init、new、copy的初始化的对象由ARC管理,isTaggedPointer小对象类型无需对内存进行管理,它的值存在指针中。除了以上提到的小对象和alloc/init、new、copy初始化的对象以外的其他对象都可以由自动释放池进行内存的管理.
**
当使用alloc/new/copy/mutableCopy开始的方法进行初始化时,会生成并持有对象(也就是不需要pool管理,系统会自动的帮他在合适位置release)

例如: NSObject *stu = [[NSObject alloc] init];
那么对于其他情况,例如

id obj = [NSMutableArray array];

这种情况会自动将返回值的对象注册到autorealeasepool,代码等效于:

@autorealsepool{
id __autorealeasing obj = [NSMutableArray array];
}

作者:Joy___
链接:https://www.jianshu.com/p/e3690f3e4675
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
**
下文中提到的栈是指数据结构--先进后出

什么是自动释放池

OC中的一种内存自动回收机制,它可以延迟加入AutoreleasePool中的变量release的时机,即当我们创建了一个对象,并把他加入到了自动释放池中时,他不会立即被释放,会等到一次runloop结束或者作用域超出{}或者超出[pool release]之后再被释放

自动释放池的使用就是将作用域中的代码包含在__AtAutoreleasePool的构造函数和析构函数中,由atautoreleasepoolobj对象对作用域的内存进行管理.
由AutoreleasePoolPage的数据结构我们可以得出,autoreleasepool是一个以AutoreleasePoolPage分页管理的双向链表。

总结

自动释放池autorelease是一个以AutoreleasePoolPage为页,进行分页管理的双向链表;页的数据结构是AutoreleasePoolPageData;每一个autoreleasepool对象只有一个哨兵(POOL_BOUNDARY),哨兵放在第一页中;每一页的大小为4096字节;每一页的前56个字节存储页的AutoreleasePoolPageData结构体数据;第一页的第56往后8个字节存储哨兵,后面存储autorelease对象,总共可以存储504个;从第二页开始,每页可以存储505个对象;objc_autoreleasepoolpush是一个查找child,递增next,创建新页的过程;objc_autoreleasepoolpop是一个查找parent,递减next,释放对象,销毁page的过程,遇到哨兵对象即停止。

POOL_BOUNDARY:哨兵,可以理解为一个标记,释放池最后一位,释放到该位置时说明对象已经释放完成。

POOL_BOUNDARY 插入的位置:

每次 @autoreleasepool{} 一开始都会最终调用 AutoreleasePoolPage 的 push 方法,则每次都会入栈一个POOL_BOUNDARY哨兵对象用来标记 autoreleasepool 的起始位置。

作者:6ffd6634d577
链接:https://www.jianshu.com/p/416f1c3d24ae
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

int main(){

    @autoreleasepool {
        NSObject *obj = [[NSObject alloc] init];
    }

}

上面代码实际可以认为转化为下面的代码进行执行

int main(){
 {
        void * atautoreleasepoolobj;
    //  objc_autoreleasePoolPush()第一个内容就是哨兵对象,即atautoreleasepoolobj 哨兵地址
        atautoreleasepoolobj = objc_autoreleasePoolPush();
   
        NSObject *obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
        // 释放对象时到哨兵地址结束。
        objc_autoreleasePoolPop(atautoreleasepoolobj)
        
    }
}

自动释放池什么时候创建(主线程),什么时候销毁?(ARC)

分两种情况:
如果不是使用autoreleasepool,

 NSObject *obj = [[NSObject alloc] init];

则是在出了作用域就释放
如果是

 @autoreleasepool {
       //对象
   }

则是
每一次运行循环执行后,也就是每当事件被触发时都会创建自动释放池。在程序执行的过程中,所有autorelease的对象在出了作用域之后会被添加到最近创建的自动释放池中。运行循环结束前会释放自动释放池,还有池子满了也会销毁。

Runloop和Autorelease

iOS 在主线程的Runloop 注册了2个Observer(还有其他的Observer)
一个Observer监听kCFRunloopEntry事件,会调用objc_autoreleaePoolPush()
另外Observer一个监听两个事件:
kCFRunloopBeforeWaiting事件,会调用objc_autoreleaePoolPop()、objc_autoreleaePoolPush(),
kCFRunloopBeforeExit事件,会调用objc_autoreleaePoolPop()

都说子线程不主动获取没有runloop,而每个runloop都会在开始创建一个自动释放池,那没有runloop,子线程是否有自动释放池呢?比如下面这个

image

那么,

  1. 子线程是否有自动释放池呢
    在子线程中原本是没有自动释放池的,但是如果有runloop或者autorelease对象的时候,就会自动的创建自动释放池。
    苹果官方文档中有介绍,每一个线程都会维护自己的Autoreleasepool 栈,所以子线程虽然默认没有开启Runloop,但是依然存在
    Autoreleasepool,在子线程退出的时候会去释放autorelease 对象。
    如果子线程中没有创建Autoreleasepool,而一旦产生Autorelease对象,就会自动创建AutoreleasePoolPage页,并将对象加入到其栈中,所以一般情况下,子线程中即使不手动添加自动释放池,也不会产生内存泄露。

这篇文章Autorelease 对象的内存管理,作者给出了明确的答案: OS X 10.9+和 iOS 7+不会造成内存泄露,当子线程未开启runloop的时候而你用到了autorelease对象会调用 autoreleaseNoPage 方法,这个方法会为你创建一个hotpage(可以理解为当前正在使用的autoreleasePoolPage),并调用page->add(obj)将autorelease对象加入到自动释放池中,这样就不会造成内存泄露了!
这个是autoreleaseNoPage的源码:
————————————————
版权声明:本文为CSDN博主「不在下雪天」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lixuezhi86/article/details/81813926

  1. 这个obj究竟在什么时候释放呢?
    每个autorelease创建的时候都会监听当前线程的销毁方法,在线程退出时调用tls_dealloc方法。
    然后这个tls_dealloc会调用autoreleasePoolPop清楚所有autorelease对象。
  2. 是在子线程销毁后释放,还是与子线程的生命周期无关?
    子线程销毁后
  • Autoreleasepool 与 Runloop 的关系
  • ARC 下什么样的对象由 Autoreleasepool 管理
  • 子线程默认不会开启 Runloop,那出现 Autorelease 对象如何处理?不手动处理会内存泄漏吗?

针对第一个问题,比较容易理解,可以看一下:ibireme深入理解RunLoop,主线程默认为我们开启 Runloop,Runloop 会自动帮我们创建Autoreleasepool,并进行Push、Pop 等操作来进行内存管理

第二个问题,ARC 下什么样的对象由 Autoreleasepool 管理呢?大多数人的回答是:“都会由 pool 进行管理”。其实并不是这样的,对于普通的对象是由编译器在合适的地方为我们 Realease 了。针对这个问题,我已经总结过:引用计数带来的一次讨论,是参考了经典的《iOS与OS X多线程和内存管理 》这本书。

针对第三个问题,感觉比较难以回答,需要很细致的读过 Runtime 、Autoreleasepool 的源码才可以。我也是参考了 StackOverFlow 的回答:does NSThread create autoreleasepool automaticly now?。我再来简单阐述下,在子线程你创建了 Pool 的话,产生的 Autorelease 对象就会交给 pool 去管理。如果你没有创建 Pool ,但是产生了 Autorelease 对象,就会调用 autoreleaseNoPage 方法。在这个方法中,会自动帮你创建一个 hotpage(hotPage 可以理解为当前正在使用的 AutoreleasePoolPage,如果你还是不理解,可以先看看 Autoreleasepool 的源代码,再来看这个问题 ),并调用 page->add(obj)将对象添加到 AutoreleasePoolPage 的栈中,也就是说你不进行手动的内存管理,也不会内存泄漏啦!StackOverFlow 的作者也说道,这个是 OS X 10.9+和 iOS 7+ 才加入的特性。并且苹果没有对应的官方文档阐述此事,但是你可以通过源码了解。这里张贴部分源代码:

static __attribute__((noinline))
id *autoreleaseNoPage(id obj)
{
    // No pool in place.
    // hotPage 可以理解为当前正在使用的 AutoreleasePoolPage。
    assert(!hotPage());

    // POOL_SENTINEL 只是 nil 的别名
    if (obj != POOL_SENTINEL  &&  DebugMissingPools) {
        // We are pushing an object with no pool in place, 
        // and no-pool debugging was requested by environment.
        _objc_inform("MISSING POOLS: Object %p of class %s "
                     "autoreleased with no pool in place - "
                     "just leaking - break on "
                     "objc_autoreleaseNoPool() to debug", 
                     (void*)obj, object_getClassName(obj));
        objc_autoreleaseNoPool(obj);
        return nil;
    }

    // Install the first page.
    // 帮你创建一个 hotpage(hotPage 可以理解为当前正在使用的 AutoreleasePoolPage
    AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
    setHotPage(page);

    // Push an autorelease pool boundary if it wasn't already requested.
    // POOL_SENTINEL 只是 nil 的别名,哨兵对象
    if (obj != POOL_SENTINEL) {
        page->add(POOL_SENTINEL);
    }

    // Push the requested object.
    // 把对象添加到 自动释放池 进行管理
    return page->add(obj);
}

作者:Joy___
链接:https://www.jianshu.com/p/f87f40592023
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

需要手动添加AutoreleasePool的情况

. 编写的不是基于UI框架的程序,例如命令行工具;
. 通过循环方式创建的大量的临时对象;
. 使用非Cocoa 程序创建的子线程。

什么对象会加入自动释放池

alloc、new、copy、mutableCopy等持有对象的方法,不会加入到autoreleasepool;而其他不持有对象的方法则通过objc_autoreleaseReturnValue和objc_retainAutoreleaseReturnValue来判断是否需要加入到autoreleasepool,这是编译器的优化。

iOS5及之后,关键字__weak修饰的对象,直接调用release,不会加入autoreleasepool。

id指针(id )和对象指针(如NSError*),会自动加上关键字__autoreleasing,加入到autoreleasepool。

使用容器的block版本的枚举器时,内部会自动添加一个autoreleasepool:

[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
// 这里被一个局部@autoreleasepool包围着
}];
当然,在普通for循环和for in循环中没有,for循环中遍历会产生大量autorelease变量时,就需要手动添加局部autoreleasepool。

不错的博客:
https://www.jianshu.com/p/7bd2f85f03dc

作者:boy丿log
链接:https://www.jianshu.com/p/d891f13d92ca
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

参考:https://zhuanlan.zhihu.com/p/321687906
作者:赵哥窟
链接:https://www.jianshu.com/p/9dad9c3247ed
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

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

推荐阅读更多精彩内容