前言
在iOS
中由于有ARC
的存在,经常有博客会讲到循环时的内存堆积问题,主动参加autoreleasepool
能够避免内存延时开释,降低内存峰值,这也是面试中优化方面常会被考察的点,所以项目中基本上凡是有有较多数据循环的当地都会被手动参加autoreleasepool
,如果你没加,在code review的时分都会被以为为“没有必定内存方面的思考”(菜鸡)
本文不会去讲autoreleasepool
的原理数据结构等常识(许多优异的博客都讲的很好了),只会针对autoreleasepool
用法这一点进行探讨
那么问题来了,你加的autoreleasepool真的有用么??
下面截取一段某大厂对外供给的demo代码片段(已做简化)
刚好由于需求看到了这段代码实在是太典型了,也是本身项目中众多循环代码的缩影,所以促成了想写这篇帖子的想法
槽点:创立的item
会被加到数组中,本身就不会开释,为什么要加autoreleasepool
呢
那去掉add
操作后代码如下
看起来就比较像许多示例代码了,实际测验下来也并没有发生内存堆积导致的峰值升高
那把autoreleasepool
去掉呢?代码如下
测验下发现也没有发生内存堆积导致的峰值升高
原因:许多人错误的了解了ARC
导致以为创立的目标实际都是归autoreleasepool
去办理的
实际上直接经过alloc 、new
等创立的目标都是靠ARC
主动刺进release
开释的,跟autoreleasepool
没啥联系,只要被autorelease
的目标才会被参加到主动开释池中受autoreleasepool
的办理,代码如下
验证下确实会由于autorelease
的开释机制(RunLoop
)导致因内存堆积导致的峰值升高,此刻手动参加autoreleasepool
进行办理,就不会有内存堆积导致的峰值升高的问题了,代码如下
总结如下
但是实际开发中绝大多数目标都是直接经过alloc 、new
等创立出来的,目标都是靠ARC
主动刺进release
开释的,手动autoreleasepool
+__autoreleasing
就显得有点多此一举了
那么问题又来了,__autoreleasing究竟应该啥时分用??
目前大厂面试都有必定的数据结构与算法方面的查核,现在来看下这一段代码,结构一条5W数据的链表
代码很简单看起来也没什么问题
运行一下会发现最终crash了
原因:头结点析构后会开释本身持有的属性(cxx_destruct
,这儿不去讲dealloc
的流程 网上有许多优异的帖子)导致next
指向的node
析构…不断的析构导致stack overflow
引发的EXC_BAD_ACCESS
我猜这可能也是根据引用计数机制的iOS
没有供给链表(NSNode
)这种数据结构的一个原因,经过链表完成的栈、行列也没有直接供给(NSMutableArray
底层的环型数组机制也能保障头尾操作是O(1)的,能够作为栈、行列的平替)
那么如何处理呢??
能够在node
目标的dealloc
办法中手动制空next
指针让next
指向的node
目标析构,其原理是将开释节点提早了,不会导致stack overflow
(详细参考dealloc
流程),但也只是治标不治本,将数组量级增大仍是会导致stack overflow
最终处理方案是将next
参加到主动开释池中,由主动开释池办理其开释,就不是造成因链式析构导致的stack overflow
问题了(详细参考autoreleasepool
开释流程)
RAC
代码库里有更精密的完成(超越某一阈值后参加到autoreleasepool
中),代码如下
感触
1.期望这篇文章能对你有所协助,能更精准的运用autoreleasepool
并对ARCautorelease
等机制有更深化的了解
2.从业多年一向有想写点技术博客的激动,但受限于没想到写什么(iOS
相关的帖子实在是太多了,基本你想写什么都有很优异的帖子存在了)和繁忙的工作(懒),这次因疫情原因总算鼓起勇气尝试了一下,期望能为以后打好根底
愿疫情早日完毕、世界和平
update
为SDWebImage提了pr#3388,已被兼并