[关闭]
@iwanglong 2018-12-27T06:47:19.000000Z 字数 4195 阅读 237

自动释放池

iOS


面试了两家,都问了自动释放池相关的知识,作业帮问的是(runloop与自动释放池的关系)、头条问的是(说一下自动释放池的原理以及一些细节)。当面试问到自动释放池的时候,首先想到的应该是OC的内存管理。

首先这里先说一下RunLoop与自动释放池的关系吧

我是按照网上总结的RunLoop与自动释放池回答的面试官。当回答完这个以后,那么好接下来说一下自动释放池是怎么释放的释放时机是什么,我当时回答是在autoreleasepool{}花括号结束释放。这个回答不是面试官想要的答案, 我理解应该 内存管理引用计数器 的问题了吧?

autoreleased对象什么时候释放

下面通过3个例子来看一下到底什么时候释放的:

  1. __weak NSString *string_A = nil;
  2. __weak NSString *string_B = nil;
  3. __weak NSString *string_C = nil;
  4. - (void)viewDidLoad {
  5. [super viewDidLoad];
  6. NSString *StrA = [NSString stringWithFormat:@"-----场景1--------"];
  7. string_A = StrA;
  8. @autoreleasepool{
  9. NSString *StrB = [NSString stringWithFormat:@"-----场景2--------"];
  10. string_B = StrB;
  11. }
  12. NSString *StrC = nil;
  13. @autoreleasepool{
  14. StrC = [NSString stringWithFormat:@"-----场景3--------"];
  15. string_C = StrC;
  16. }
  17. NSLog(@"******viewDidLoad*******%@",string_A);
  18. NSLog(@"******viewDidLoad*******%@",string_B);
  19. NSLog(@"******viewDidLoad*******%@",string_C);
  20. }
  21. - (void)viewWillAppear:(BOOL)animated{
  22. [super viewWillAppear:animated];
  23. NSLog(@"******viewWillAppear*******%@",string_A);
  24. NSLog(@"******viewWillAppear*******%@",string_B);
  25. NSLog(@"******viewWillAppear*******%@",string_C);
  26. }
  27. - (void)viewDidAppear:(BOOL)animated{
  28. [super viewDidAppear:animated];
  29. NSLog(@"******viewDidAppear*******%@",string_A);
  30. NSLog(@"******viewDidAppear*******%@",string_B);
  31. NSLog(@"******viewDidAppear*******%@",string_C);
  32. }
  33. 2018-12-24 10:50:01.614715+0800 Autoreleasepool[24851:3003734] ******viewDidLoad*******-----场景1--------
  34. 2018-12-24 10:50:01.614845+0800 Autoreleasepool[24851:3003734] ******viewDidLoad*******(null)
  35. 2018-12-24 10:50:01.614964+0800 Autoreleasepool[24851:3003734] ******viewDidLoad*******-----场景3--------
  36. 2018-12-24 10:50:01.615169+0800 Autoreleasepool[24851:3003734] ******viewWillAppear*******-----场景1--------
  37. 2018-12-24 10:50:01.615303+0800 Autoreleasepool[24851:3003734] ******viewWillAppear*******(null)
  38. 2018-12-24 10:50:01.615390+0800 Autoreleasepool[24851:3003734] ******viewWillAppear*******(null)
  39. 2018-12-24 10:50:01.618742+0800 Autoreleasepool[24851:3003734] ******viewDidAppear*******(null)
  40. 2018-12-24 10:50:01.618871+0800 Autoreleasepool[24851:3003734] ******viewDidAppear*******(null)
  41. 2018-12-24 10:50:01.619000+0800 Autoreleasepool[24851:3003734] ******viewDidAppear*******(null)

在没有手加Autorelease Pool的情况下,Autorelease对象是在当前的runloop迭代结束时释放的,而它能够释放的原因是系统在每个runloop迭代中都加入了自动释放池Push和Pop

Autorelease原理

AutoreleasePoolPage

ARC下,我们使用@autoreleasepool{}来使用一个AutoreleasePool,随后编译器将其改写成下面的样子:

  1. void *context = objc_autoreleasePoolPush();
  2. // {}中的代码
  3. objc_autoreleasePoolPop(context);

而这两个函数都是对AutoreleasePoolPage的简单封装,所以自动释放机制的核心就在于这个类。
AutoreleasePoolPage

所以,若当前线程中只有一个AutoreleasePoolPage对象,并记录了很多autorelease对象地址时内存如下图:
autorelease对象
图中的情况,这一页再加入一个autorelease对象就要满了(也就是next指针马上指向栈顶),这时就要执行上面说的操作,建立下一页page对象,与这一页链表连接完成后,新page的next指针被初始化在栈底(begin的位置),然后继续向栈顶添加新对象。

所以,向一个对象发送- autorelease消息,就是将这个对象加入到当前AutoreleasePoolPage的栈顶next指针指向的位置

释放时刻

每当进行一次objc_autoreleasePoolPush调用时,runtime向当前的AutoreleasePoolPage中add进一个哨兵对象,值为0(也就是个nil),那么这一个page就变成了下面的样子:
autoreleasepoolPush

objc_autoreleasePoolPush的返回值正是这个哨兵对象的地址,被objc_autoreleasePoolPop(哨兵对象)作为入参,于是:

刚才的objc_autoreleasePoolPop执行后,最终变成了下面的样子:
autoreleasepoolPop

更多资料

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注