@iwanglong
2018-12-27T06:47:19.000000Z
字数 4195
阅读 237
iOS
面试了两家,都问了自动释放池相关的知识,作业帮问的是(runloop与自动释放池的关系)、头条问的是(说一下自动释放池的原理以及一些细节)。当面试问到自动释放池的时候,首先想到的应该是OC的内存管理。
我是按照网上总结的RunLoop与自动释放池回答的面试官。当回答完这个以后,那么好接下来说一下自动释放池是怎么释放的释放时机是什么,我当时回答是在autoreleasepool{}花括号结束释放。这个回答不是面试官想要的答案, 我理解应该 内存管理引用计数器 的问题了吧?
下面通过3个例子来看一下到底什么时候释放的:
__weak NSString *string_A = nil;__weak NSString *string_B = nil;__weak NSString *string_C = nil;- (void)viewDidLoad {[super viewDidLoad];NSString *StrA = [NSString stringWithFormat:@"-----场景1--------"];string_A = StrA;@autoreleasepool{NSString *StrB = [NSString stringWithFormat:@"-----场景2--------"];string_B = StrB;}NSString *StrC = nil;@autoreleasepool{StrC = [NSString stringWithFormat:@"-----场景3--------"];string_C = StrC;}NSLog(@"******viewDidLoad*******%@",string_A);NSLog(@"******viewDidLoad*******%@",string_B);NSLog(@"******viewDidLoad*******%@",string_C);}- (void)viewWillAppear:(BOOL)animated{[super viewWillAppear:animated];NSLog(@"******viewWillAppear*******%@",string_A);NSLog(@"******viewWillAppear*******%@",string_B);NSLog(@"******viewWillAppear*******%@",string_C);}- (void)viewDidAppear:(BOOL)animated{[super viewDidAppear:animated];NSLog(@"******viewDidAppear*******%@",string_A);NSLog(@"******viewDidAppear*******%@",string_B);NSLog(@"******viewDidAppear*******%@",string_C);}2018-12-24 10:50:01.614715+0800 Autoreleasepool[24851:3003734] ******viewDidLoad*******-----场景1--------2018-12-24 10:50:01.614845+0800 Autoreleasepool[24851:3003734] ******viewDidLoad*******(null)2018-12-24 10:50:01.614964+0800 Autoreleasepool[24851:3003734] ******viewDidLoad*******-----场景3--------2018-12-24 10:50:01.615169+0800 Autoreleasepool[24851:3003734] ******viewWillAppear*******-----场景1--------2018-12-24 10:50:01.615303+0800 Autoreleasepool[24851:3003734] ******viewWillAppear*******(null)2018-12-24 10:50:01.615390+0800 Autoreleasepool[24851:3003734] ******viewWillAppear*******(null)2018-12-24 10:50:01.618742+0800 Autoreleasepool[24851:3003734] ******viewDidAppear*******(null)2018-12-24 10:50:01.618871+0800 Autoreleasepool[24851:3003734] ******viewDidAppear*******(null)2018-12-24 10:50:01.619000+0800 Autoreleasepool[24851:3003734] ******viewDidAppear*******(null)
在没有手加Autorelease Pool的情况下,Autorelease对象是在当前的runloop迭代结束时释放的,而它能够释放的原因是系统在每个runloop迭代中都加入了自动释放池Push和Pop
ARC下,我们使用@autoreleasepool{}来使用一个AutoreleasePool,随后编译器将其改写成下面的样子:
void *context = objc_autoreleasePoolPush();// {}中的代码objc_autoreleasePoolPop(context);
而这两个函数都是对AutoreleasePoolPage的简单封装,所以自动释放机制的核心就在于这个类。

所以,若当前线程中只有一个AutoreleasePoolPage对象,并记录了很多autorelease对象地址时内存如下图:
图中的情况,这一页再加入一个autorelease对象就要满了(也就是next指针马上指向栈顶),这时就要执行上面说的操作,建立下一页page对象,与这一页链表连接完成后,新page的next指针被初始化在栈底(begin的位置),然后继续向栈顶添加新对象。
所以,向一个对象发送- autorelease消息,就是将这个对象加入到当前AutoreleasePoolPage的栈顶next指针指向的位置
每当进行一次objc_autoreleasePoolPush调用时,runtime向当前的AutoreleasePoolPage中add进一个哨兵对象,值为0(也就是个nil),那么这一个page就变成了下面的样子:

objc_autoreleasePoolPush的返回值正是这个哨兵对象的地址,被objc_autoreleasePoolPop(哨兵对象)作为入参,于是:
刚才的objc_autoreleasePoolPop执行后,最终变成了下面的样子:
