内存管理是程序设计中很重要的一部分,程序在运行的过程中消耗内存,运行结束后释放占用的内存。如果程序运行时一直分配内存而不及时释放无用的内存,会造成这样的后果:程序占用的内存越来越大,直至内存消耗殆尽,程序因无内存可用导致崩溃,这样的情况我们称之为内存泄漏。
理解好内存管理,能让我们写出更有质量的代码。
iOS 内存管理的核心是引用计数。
引用计数
追溯历史,我们知道引用计数经历了 ARC 和 MRC 两个时代。那么,什么是 ARC 呢?
顾名思义,自动引用计数(ARC, Automatic reference counting)是指内存管理中对引用采取自动计数的技术。
在 Objective-C 中采用 Automatic Reference Counting(ARC) 机制,让编译器来进行内存管理。在新一代 Apple LLVM 编译器中设置 ARC 为有效状态,就无需再次键入 retain 或者 release 代码,这在降低程序崩溃、内存泄漏等风险的同时,很大程度上减少了开发程序的工作量。编译器完全清楚目标对象,并能立刻释放那些不再被使用的对象。如此一来,应用程序将具有可预测性,且能流畅运行,速度也将大幅提升。
举个栗子🌰
为了更好的解释什么是 引用计数 ,我们可以用一个生活中的例子来说明。下面以开关房间的灯为例子来说明。
假设办公室里的照明设备只有一个。上班进入办公室的人需要照明,所以要把灯打开。而对于下班离开办公室的人来说,已经不需要照明了,所以要把灯关掉。若是很多人上下班,每个人都开灯或是关灯,那么办公室的情况又将如何呢?最早下班离开的人如果关了灯,办公室里还没走的所有人都将处于一片黑暗之中。
解决这一问题的办法是使办公室在还有至少1人的情况下保持开灯状态,而在无人时保持关灯状态。
(1)最早进入办公室的人开灯
(2)之后进入办公室的人,需要照明。
(3)下班离开办公室的人,不需要照明。
(4)最后离开办公室的人关灯(此时已无人需要照明)。
为判断是否还有人在办公室里,这里导入计数功能来计算“需要照明的人数”。下面让我们来看看这一功能是如何运作的吧。
(1)第一个人进入办公室,“需要照明的人数”加1。计数值从0变成了1,因此要开灯。
(2)之后每当有人进入办公室,“需要照明的人数”就加1。如计数值从1变成2。
(3)每当有人下班离开办公室,“需要照明的人数”就减1。如计数值从2变成1。
(4)最后一个人下班离开办公室时,“需要照明的人数”减1。计数值从1变成了0,因此要关灯。
这样就能在不需要照明的时候保持关灯状态。办公室中仅有的照明设备得到了很好的管理。
在 Objective-C中,“对象”相当于办公室的照明设备。在现实世界中办公室的照明设备只有一个,但在 Objective-C 的世界里,虽然计算机资源有限,但一台计算机可以同时处理好几个对象。
小结
对办公室照明设备所做的动作和对 Objective-C 的对象所做的动作
| 对照明设备所做的动作 | 对 Objective-C 的对象所做的动作 |
|---|---|
| 开灯 | 生成对象 |
| 需要照明 | 持有对象 |
| 不需要照明 | 释放对象 |
| 关灯 | 废弃对象 |
使用计数功能计算需要照明的人数,使办公室的照明得到了很好的管理。同样,使用引用计数功能,对象也就能够得到很好的管理,这就是 Objective-C的内存管理。
内存管理原则
- 自己生成的对象,自己所持有。
使用 alloc、new、copy、mutableCopy 名称开头的方法名意味着自己生成的对象只有自己持有。
copy 方法利用基于 NSCopying 方法约定,由各类实现的 copyWithZone: 方法生成并持有对象的副本。与 copy 方法类似,mutableCopy 方法利用基于 NSMutableCopying 方法约定,由各类实现的 mutableCopyWithZone: 方法生成并持有对象的副本。两者的区别在于,copy 方法生成不可变更的对象,而 mutableCopy 方法生成可变更的对象。这类似于 NSArray 类对象与
NSMutableArray 类对象的差异。用这些方法生成的对象,虽然是对象的副本,但同
alloc、new 方法一样,在“自己生成并持有对象”这点上没有改变。
- 非自己生成的对象,自己也能持有。
通过 retain 方法,非自己生成的对象跟用 alloc、new、copy、mutableCopy 方法生成并持有的对象一样,成为了自己所持有的。
- 不再需要自己持有的对象时释放
自己持有的对象,一旦不再需要,持有者有义务释放该对象。

- 非自己持有的对象无法释放。
对于用 alloc、new、copy、mutableCopy 方法生成并持有的对象,或是用 retain方法持有的对象,由于持有者是自己,所以在不需要该对象时需要将其释放。而由此以外所得到的对象绝对不能释放。倘若在应用程序中释放了非自己所持有的对象就会造成崩溃。例如自己生成并持有对象后,在释放完不再需要的对象之后再次释放。