iPhone6/6p 在发布动态时选择特别多张大尺寸的图片(如网络下载的长图)时,会出现闪退的现象
iOS 系统会根据设备硬件条件为每个App设定了一个内存使用上限,若App申请的内存超高该限制则会crash,也就是我们常说的OOM (Out Of Memory)。在6和6p手机上该限制的值为650M,当用户发送9张大图时,处理+上传图片时App占用内存会达到650M,触发了OOM崩溃
WX20220209-124605@2x.png
发送多图动态时降低内存占用,以防止低端设备如6、6p在发送动态过程中因内存峰值造成OOM闪退
使用Instruments检测内存峰值发生的位置
操作路径:进入发布页-点击相册icon-选择9张图-点击确定。
该阶段内存占用Instruments 检测的结果如下图,共占用198.84M(实验两次数值一致)
图1.选择前后创建并回收的内存
图2.从相册获取对应的所有原始image信息,然后生成placeHolderImage
图3.裁剪原始图片信息image生成用于展示的image
图4.内存占用都出现在UIImage的裁剪方法中
操作路径:点击发布
该阶段内存占用Instruments 检测的结果如下图,共占用330M(实验两次数值一致)
图1. 发布过程中共申请并释放内存(红框中占比比较大,共计330M左右)
图2.对应图1红框中第一项189.84M(21.09M*9)
图3.对应图1红框中第二项139.22M(23.2M*6)
图4.对应图2中内存占用为压缩图片
图5. 调用到图4中的上层方法
根据上面Instruments的截图可以看出,发布动态整个过程的内存峰值会出现两次,分别为选择图片阶段和发布图片动态阶段
并发从相册中请求多张图片,但未限制最大并发数,对于低端设备来说同时开启多条线程从相册异步加载图片,会在瞬间达到内存峰值
从相册请求图片时请求的是原图的 data 数据,[UIImage imageWithData:data]得到图片,再次进行适当裁剪,裁剪图片是需要将整个图片进行解码,对于普通的SRGB图片占用内存为widthheight4 bytes
对于问题1,可以设置最大并发数,这个值可以根据设备的情况来定,避免内存峰值过于大导致OOM
对于问题2,可以使用 requestImageForAsset:targetSize:contentMode:options:resultHandler:api 来请求合适尺寸的图片,而非直接请求原图。一来请求图片尺寸下降带来图片加载耗时降低和内存占用减少,二来避免了在原图基础上解码进行裁剪,也减少内存的占用和cpu的使用
在动态图片发送阶段,动态的状态会发生多次变化(发送中、发送失败、发送成功)都会讲PUGMoment进行入库处理,保留草稿以保证当发送失败后可以再次发送。使用TXTDB入库Moment时,当PUGMoment中mediaDatas会上传成功之前都会占用图片内存
从PhotoLibrary获取图片时获取请求合适的尺寸而非原图,用于显示的展位图改为从 PhotoLibray 请求合适尺寸而非通过原图裁剪
从PhotoLibrary获取图片时获取请求合适的尺寸,而非直接读取原图
用于显示的展位图改为从 PhotoLibray 请求合适尺寸,取代原有获取原图然后裁剪的方式
控制加载图片的最大并发数,减少同一时间开辟的线程数,对低性能设备友好
使用iPhone12作为测试对象,9张图尺寸均为 3024*4032,下同:
优化前后内存占用情况如下(单位:M)
优化项 | pre | after | 差值 | 峰值 |
---|---|---|---|---|
无 | 276.70 | 499.60 | 222.9 | 699.17 |
请求适合尺寸图 | 267.09 | 287.0 | 13.0 | 315.86 |
请求合适尺寸+最大并发数1 | 272.10 | 290 | 17.9 | 308.10 |
请求合适尺寸+最大并发数3 | 264.10 | 288.49 | 24.39 | 310.90 |
可以看出,限制最大并发数对降低内存峰值没有太大帮助,但是可以帮助我们减少线程数量
发布阶段,优化前后内存占用情况(单位:M):
优化项 | pre | after | 差值 | 峰值 |
---|---|---|---|---|
无 | 482.10 | 310 | -172.10 | 817.56 |
合适尺寸 | 288.06 | 275.66 | -12.4 | 519.39 |
基于以上,我们使用苹果官方性能分析工具成功定位到动态发送过程中的内存高占用问题,并定位到具体的代码。除了本文章中检测内存峰值,Instruments 还有很多其他性能检测功能,比如内存泄漏、启动耗时、方法耗时、掉帧等。作为开发者我们需要时刻关心应用的性能表现,因为性能对于应用的使用体验至关重要,定期使用 Instruments 跑一遍性能指标,可以很好地帮助我们发现和解决性能问题。
iOS Memory Deep Dive