Swift开发中如何判断是否需要使用autoreleasepool?
由于苹果API并未开源,相关说明也没有写入官方文档,使用Swift开发时我们无法得知返回的对象是否是标记了autorelease的Objective-C对象,因此很难明确什么时候应该使用autoreleasepool。
苹果官方内存管理文档中有如下说明:
如果你编写的循环会生成大量临时对象,你可以在循环内部使用autorelease pool代码块,在下一次迭代前释放这些对象。在循环中使用autorelease pool代码块有助于降低应用的最大内存占用。
下面是两段对照代码示例:
未使用autoreleasepool的代码
for ... { FileManager.default.copyItem CGImageSourceCreateWithURL CGImageSourceCopyPropertiesAtIndex CGImageSourceCreateThumbnailAtIndex CGImageDestinationCreateWithURL CGImageDestinationFinalize }
使用autoreleasepool的代码
for ... { autoreleasepool { FileManager.default.copyItem CGImageSourceCreateWithURL CGImageSourceCopyPropertiesAtIndex CGImageSourceCreateThumbnailAtIndex CGImageDestinationCreateWithURL CGImageDestinationFinalize } }
针对密集循环场景对两段代码做对比测试后,Xcode内存报告显示二者的内存使用模式没有显著差异,这里给出判断是否需要使用autoreleasepool的几个可靠指导原则:
1. 看调用的API来源
只要调用的是从Objective-C桥接的Foundation、CoreFoundation类接口,尤其是文件读写、图片编解码、大数据批量处理这类会生成大体积临时对象的接口,都可以优先考虑使用autoreleasepool。你测试时没有看出差异,大概率是测试场景的压力不够,换成更大的资源、更长的循环就能看出区别,比如FileHandle.read这类接口,内部实现默认会返回autorelease的NSData对象,不加pool很容易出现内存持续上涨的问题。
2. 看循环的执行规模
如果循环总次数只有几十上百次,且单次循环生成的临时对象体积很小,完全可以不用加autoreleasepool。但如果是成千上万次的密集循环,或者单次循环就会生成MB级别的临时对象,哪怕测试时内存没有明显上涨,也建议加上,避免极端场景下内存峰值过高被系统强制杀掉。
3. 用Instruments工具确认
如果拿不准,可以用Instruments的Allocations工具,勾选Autoreleased Objects过滤选项跑一遍业务流程,如果观察到autorelease对象的内存占用随着循环持续上涨、没有在迭代间隙释放,就说明需要加autoreleasepool。Xcode自带的内存报告采样粒度较粗,小幅度的内存波动不会显示出来,不能作为判断的唯一依据。
另外要注意,加autoreleasepool本身几乎没有额外的性能损耗,符合场景时添加属于稳赚不赔的优化操作。
内容的提问来源于stack exchange,提问作者Cheok Yan Cheng

