本文章将记录Objective-C中内存管理的相关资料,如有错误欢迎指出~
iOS的内存管理一般指的是OC对象的内存管理,因为OC对象分配在堆内存,堆内存需要程序员自己去动态分配和回收;基础数据类型(非OC对象)则分配在栈内存中,超过作用域就会由系统检测回收。如果我们在开发过程中,对内存管理得不到位,就有可能造成内存泄露。
Objective-C中提供了两种内存管理机制:MRC(MannulReference Counting)和 ARC(Automatic Reference Counting),MRC指的是手动内存管理,在开发过程中需要开发者手动去编写内存管理的代码;ARC指的是自动内存管理,在此内存管理模式下由LLVM编译器和OC运行时库生成相应内存管理的代码。
引用计数
不管是MRC 还是 ARC ,都是通过对引用计数来进行内存管理的。
- 创建一个新对象的时候,它的引用计数为 1
- 当有一个新的指针指向这个对象时,其引用计数加 1
- 当某个指针不再指向这个对象是,其引用计数减 1,当对象的引用计数变为 0 时,说明这个对象不再被任何指针指向了,这个时候就可以将对象销毁,回收内存。
当一个对象使用完没有释放,此时其引用计数永远大于1。该对象就会一直占用其分配在堆内存的空间,就会导致内存泄露。内存泄露到一定程度有可能导致内存溢出,进而导致程序崩溃。
MRC(MannulReference Counting)
先了解下 内存管理的思想
- 自己生成的对象,自己持有。
- 非自己生成的对象,自己也能持有。
- 不再需要自己持有的对象时释放对象。
- 非自己持有的对象无法释放。
从上面的思想来看,我们对对象的操作可以分为三种:创建,持有,释放,再加上废弃,一共有四种。它们所对应的Objective-C的方法和引用计数的变化是:
对象操作 | Objecctive-C方法 | 引用计数的变化 |
---|---|---|
生成并持有对象 | alloc/new/copy/mutableCopy等方法 | 1 |
持有对象 | retain方法 | 1 |
释放对象 | release方法 | -1 |
废弃对象 | dealloc方法 | 无 |
ARC (Automatic Reference Counting)
在ARC机制下,编译器就可以自动进行内存管理,减少了开发的工作量。但是仍有一些问题需要我们去注意。
循环引用(Reference Cycle)问题
引用计数这种管理内存的方式虽然很简单,但是有一个比较大的瑕疵,即它不能很好的解决循环引用问题。如下图所示:
- 对象 A 和对象 B,相互引用了对方作为自己的成员变量,只有当自己销毁时,才会将成员变量的引用计数减 1。
- 因为对象 A 的销毁依赖于对象 B 销毁,而对象 B 的销毁与依赖于对象 A 的销毁,这样就造成了我们称之为循环引用(Reference Cycle)的问题
- 这两个对象即使在外界已经没有任何指针能够访问到它们了,它们也无法被释放。
不止两对象存在循环引用问题,多个对象依次持有对方,形式一个环状,也可以造成循环引用问题,而且在真实编程环境中,环越大就越难被发现。下图是 4 个对象形成的循环引用问题。
那该怎么解决循环引用的问题呢?使用弱引用 (weak reference) 的办法。
弱引用
使用弱引用来持有对象,弱引用虽然持有对象,但是并不增加引用计数,这样就避免了循环引用的产生。
在 iOS 开发中,弱引用通常在 delegate 模式中使用。举个例子来说,
- 两个 ViewController A 和 B,ViewController A 需要弹出 ViewController B,让用户输入一些内容,当用户输入完成后,ViewController B 需要将内容返回给 ViewController A。
- 这个时候,View Controller 的 delegate 成员变量通常是一个弱引用,以避免两个 ViewController 相互引用对方造成循环引用问题,
弱引用的实现原理是:
- 系统对于每一个有弱引用的对象,都维护一个表来记录它所有的弱引用的指针地址。
- 当一个对象的引用计数为 0 时,系统就通过这张表,找到所有的弱引用指针,继而把它们都置成 nil。