前言:ARC
内存管理需要说说ARC:
ARC
(Automatic Reference Counting):自动引用计数内存管理。ARC是一种编译器功能,它通过LLVM编译器和Runtime协作来进行自动管理内存。
ARC
如何进行自动管理内存?
LLVM编译器会在编译时
在合适的地方为 OC 对象插入retain
、release
和autorelease
代码来自动管理对象的内存。
计算机语言分为:编译型语言、直译式语言(脚本语言)
语言 | 编译形式 | 效率 | 调试 | |
---|---|---|---|---|
编译型语言 | C++ Objective C Swift Kotlin | 先通过编译器生成机器码 机器码可以直接在 CPU 上执行 | 执行效率较高 | 调试周期长 |
直译式语言 | JavaScript Python | 不需要经过编译,在执行时 通过一个中间的解释器将代码解释为 CPU 可以执行的代码 | 执行效率低 | 编写调试方便 |
编译又分为前端编译和后端编译
1:前端编译
前端编译:主要是通过前端编译器Clang
,生成中间码。Clang
主要处理:
- 预处理: 主要是
宏替换
(把宏嵌入到对应的位置),头文件的导入
、注释删除
- 词法分析: 这里把源文件中的代码转化为特殊的
标记流
,源码被分割成一个一个的字符和单词,在行尾Loc中都标记出了源码所在的对应源文件和具体行数,方便在报错时定位问题。 - 语法分析: 这一步是把词法分析生成的
标记流
,解析成一个抽象语法树,同样地,在这里面每一节点也都标记了其在源码中的位置。 - 静态分析:分析代码是否存在问题,给出错误信息和修复方案:如出现方法被调用但是未定义、定义但是未使用的变量等
- 前面的过程完了,就会生成中间代码(bitCode) IR:并会对bitcode进行各种类型的优化,将bitcode代码进行一些逻辑等价的转换,使得代码的执行效率更高,体积更小
2:后端编译
后端编译:主要由后端编译器LLVM,把优化后的中间码bitcode
编译为指定目标架构的机器码,比如X86Backend负责把bitcode编译为x86指令集的机器码
在 iOS 编译中,编译器后端其实就是 LLVM 自己提供的一套后端。它包含了 机器码生成器、链接器等工具,会对 IR
进行机器无关的代码优化,生成机器语言。
二、内存优化思路
当内存使用过高,可能就会发生OOM(Out-Of-Memory)。所以内存使用过高,系统就会回收一些内存:
-
先回收优先级极低的进程和一些正常情况下随时可回收的进程。
-
如果还有内存压力,就会杀掉后台进程以及一些后台执行的任务
-
最终还有内存压力,就会发生OOM
内存问题主要包括两个部分
- 常见循环引用导致的内存泄露
- 大量数据加载及使用导致的内存警告
内存优化也是注意处理这两个部分
1、循环引用
循环引用对App有潜在的危害,会使内存消耗过高,性能变差和Crash等,主要从以下几个方面入手
- Delegate
代理要声明为weak,不能用strong。 - NSTimer
RunLoop会强引用target,可以使用weakProxy 消息转发。使用GCD timer - Block
_weak 修饰对象 - C语法,malloc之后调用free
二、大量数据加载导致内存暴涨
在一些使用场景里,比如整个页面初始化,要分配整个使用内存,批量的图片处理,会出现一段时间内需要加载大量内容,占用过高的内存。而iOS的低内存机制就是给你一个阈值,只要你的APP超过这个数值,哪怕只是一瞬间也会直接崩溃。因此我们就需要对这种瞬间的高增幅去进行优化。我们就需要用时间去换空间,拉长整个加载步骤。
- 懒加载:时间去换空间
- 单次出现的图片使用
imageWithContentsOfFile
加载 - 在循环里面使用autoreleasepool,让临时对象及时释放
- 图片内存使用优化:使用适当尺寸的图片、及时回收图片、注意图片缩放方式
a:使用适当尺寸的图片
解压后的图片是由一个个像素点组成的。每个像素点一般有R、G、B、A(红绿蓝透明度)四个通道,每个通道是8位,因此一个像素通常占用4字节。对于一张图片,如果同样是300300分辨率的jpeg和png两张图,文件大小可能差几倍,但是渲染后的内存开销是完全一样的。
如果一张图片尺寸是300300,解压后大小是3003004。而在view的大小是6060,最终使用的是:6060*4,那么多出的内存将会抛弃。造成资源浪费。所以我们要协商好尺寸。
所以制定了一套方案,服务端将精确尺寸的图片下发到不同机型,从根本上将内存使用降低。
b:及时回收图片
单张图片占用内存不多,累计起来却非常可观。因此,当页面pop掉时,有必要清理页面内图片的内存缓存。其次,列表类的页面在滑动时,可以及时清理那些滑出屏幕图片的内存缓存。
使用imageNamed这个方法生成的UIImage对象,会在应用的bundle中寻找图片,如果找到,则Cache到系统缓存中,作为内存的cache,而程序员是无法操作cache的,只能由系统自动处理,网络图片就可以用自己的cache逻辑管理了
c:注意图片缩放方式
处理图片缩放时,直接使用UIImage会在解码时读取文件占用一部分内存,还会生成中间位图bitmap消耗大量内存,而ImageIO不存在上述两种内存消耗,只会占用最终图片大小的内存,可以使用UIGraphics相关函数
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0);
[image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
检测工具:
Instruments
MLeaksFinder