肇鑫的技术博客

业精于勤,荒于嬉

CPU还是内存,编程中的取舍之道

最近解决了咕唧存在的一个占用内存过多的问题。

以2GB内存的iPad Pro 9.7为例,如果分享扩展使用的内存超过120MB,系统就会强制将分享扩展关闭。
从用户的角度来看,就是分享扩展闪退了。

调试模式下,苹果会在Xcode中提示内存超限时代码执行的位置,并且显示为紫色的断点。

咕唧出错的地方是图片转换的部分。当用户选择图片分享时,咕唧会在后台做多件事,以适应微博和推特服务器的要求。比如:

  1. 微博服务器要求图片大小不能超过5MB。推特要求静态图片大小不超过5MB,Gif动图不超过15MB。
  2. 此外,微博服务器对于短边超过1080像素的图片还会自动缩小到1080像素。

咕唧本身也有一些设置会影响到图片。比如:

  1. 出于隐私保护的目的,咕唧有设置默认在图片上传前,会删除图片的Exif信息和GPS信息。
  2. 出于节省流量的目的,咕唧有设置默认会缩小发布到推特的图片。

综合以上的几点,咕唧在发布前,会根据用户的账户类型不同,对于图片做额外的转换和修改。因为咕唧是跨平台的,同时支持iOS/watchOS/macOS,在图片处理时我使用的是Image I/O框架。并且当时的算法是CPU优先。因为程序中会多次使用CGImageSource,于是函数中将它做为了较长时间存在的临时变量。这样的好处是可以避免多次生成它。

这么做在iOS应用中没有问题,但是在分享扩展运行的时候,有时就会因为内存占用过多而闪退。

解决思路

Image I/O是苹果提供的底层调用,这些对象与我们平时用到的对象不一样,都是不透明的,也就是只能用,不能查看细节。

既然如此,我决定将所有使用到Image I/O框架的部分封装起来,单独构造一个类,将需要的功能暴露为函数,这样从使用者的角度,就完全看不出来是否使用了Image I/O框架。

而在这个单独构造的类中,不使用CPU优先,而是使用内存优先。虽然每次操作都会使用到CGImageSource,但是我每次使用时都会重新创建它,使用结束立即释放。这样的好处就是内存中不会长期存在一个中间变量,坏处就是会额外占用一些CPU资源。

结论

改造很成功。尝试了之前会导致分享扩展崩溃的几个图片,现在都能正常分享了。