**转载请注明出处!http://joakimliu.github.io/2018/03/17/Memory-optimization/ 谢谢! **
使用了大量内存会降低性能。所以内存优化对于注重用户体验的 App 来说是比较重要的,本文讲讲内存优化中需要注意的点。
提高内存相关性能的建议
- 延时创建 每个内存的创建都会有性能消耗,但是你又不得不申请创建内存,延时到你真正需要它的时候再去创建一个好方法。比如,为了避免启动程序变慢,我们可以将减少启动时的内存创建,只创建首先展示给用户看的相关内存。譬如,我们控制器里面经常会用到懒加载,个人觉得只要某个对象是必须出现的话,就没有必要使用懒加载。
- 创建内存高效化 使用 malloc 函数,不会保证内存零初始化,当然你可以用 memset 去将内存设置为零,更好的方法是使用 calloc 去初始化内存页面,它能为内存保存所需的虚拟地址空间,还能保证所需要的页面初始化为零,它更加高效。
- 重用一些临时对象 对一些常用的或者创建比较费时的对象,可以重用它们。例如: NSDateFormatter。
- 释放不用的内存 如果没有释放不用的内存,这会引起内存泄露,这样会使 App 不能创建所需的内存。
创建内存
APP 最终会调用 malloc library 去创建内存。ARC 下,只要有一个强引用指向创建的对象,这个对象就不会被释放。如果需要创建多个,同等大小的内存块,可以用 malloc_zone_batch_malloc 函数一次性创建。还可以用内存共享啊,但是不推荐用。
内存警告
在 iOS 中,没有 backing store,而是通过强引用去释放对象。当空闲内存(free memory)下降到某个门槛时,系统会释放掉没有改变的页面,然后向应用发送低内存通知(low-memory notification)。收到通知后,我们要做一些处理,比如释放相关对象的强引用(一定要处理,否则系统可能会终止你的程序)。收到低内存通知有以下三种途径:
- 实现 application delegate 的
applicationDidReceiveMemoryWarning:
方法 - 在自定义的 UIViewController 里面重载
didReceiveMemoryWarning
方法 - 注册接受
UIApplicationDidReceiveMemoryWarningNotification
的通知
以下三种类型内存会导致内存增加后无法得到释放,得避免
- Leaked memory Inaccessible—no more pointers to it; Can’t ever be used again (内存泄露)
- Abandoned memory Still referenced, but wasted; Won’t ever be used again (循环引用)
- Cached memory Referenced and waiting; May never be used again (缓存无用内存)
那怎样检测呢?当重复某个操作时,内存不应该无限制增长。例如:
- Pushing and popping a view controller
- Scrolling in a table view
- Performing a database search
我们可以用 Instruments 来跟踪分析
缓存和可净化的内存
缓存和可净化的内存适用于那些需要大量内存或者大量时间的开发者,毕竟 IO 操作也比较费时。我们可以缓存那些很难去计算的具有临时数据的对象。当然如果内存紧张的话,还是会被移除掉的,还得重新处理。可以用 NSCache 和 NSPurgeableData
跟踪内存的使用
用 Instruments 去跟踪分析内存的使用,大概有四种类型:
- The ObjectAlloc instrument records and displays a history of all memory allocations since the launch of your application.
- The Leaks instrument looks for allocated memory that is no longer referenced by your program’s code;
- The Shared Memory instrument monitors the opening and unlinking of shared memory regions.
- The Memory Monitor instrument measures and records the system’s overall memory usage.
这里贴一张在 WWDC 性能相关 session 上出现了几次的性能分析优化处理流程图。复现问题,用工具去检查,然后猜测调整测试,直到问题得以解决。
总结
- 由于设备的内存比较小,所以我们要尽可能的用最小的内存,在各种设备上都得测试,尤其是内存受限的设备。
- 用在 Instruments 跟踪的时候,在发现问题处做标记,用类名来筛选。
- 用 @autoreleasepool 处理大循环,并且里面处理内存数据。例如,在一万次循环里面读取图片。
- 循环引用,尤其是 block。
- 内存泄露。
- 给已释放的对象发消息会导致闪退
- NSNotificationCenter and KVO
- 与 CF 相关对象不正确的 bridge 桥接
- __unsafe_unretained 引用
- __autoreleasing NSError* and @autoreleasepool
- 图片的读取采用正确的方式
imageNamed:
会缓存图片,用它读取频繁用到的小图片,最恶心的是不知道它到底什么时候释放。imageWithContentsOfFile:
不缓存图片,适合一次性读取的大图片。