在 MRC(Manual Reference Counting)时代,判定“谁负责释放”并不看心情,而是遵循一套极其严苛的、基于方法名关键字的黄金法则。
这套规则由苹果定义,本质上是把 C 语言对内存管理的模糊性,转化为了一套语义契约。
1. 黄金法则:看方法名开头
在 MRC 下,你是否“拥有”一个对象,完全取决于你获取该对象时调用的方法名。
规则 A:你拥有对象(需要你手动 release 或 autorelease)
如果你通过以下关键字开头的方法创建/获取对象,你就获得了该对象的所有权:
alloc(例如:[[NSObject alloc] init])new(例如:[NSObject new])copy(例如:[someArray copy])mutableCopy(例如:[someString mutableCopy])
判定标准: 只要看到这四个词,你的代码逻辑里必须对应出现一个
release,否则就是内存泄漏。
规则 B:你不拥有对象(禁止你手动 release)
如果你通过其他任何方法获取对象(比如类工厂方法),你不拥有该对象:
- 例如:
[NSString stringWithFormat:...] - 例如:
[NSArray array] - 例如:
[view viewWithTag:1]
判定标准: 这些对象通常已经被标记了
autorelease。如果你手欠去release它,会导致过早释放(Over-release),程序直接 Crash。
2. 谁负责释放?—— 作用域原则
判定“谁负责”的准确规则只有一条: “谁污染,谁治理;谁持有,谁负责。”
-
持有(Ownership Transfer): 如果你不想让一个“不拥有”的对象消失,你必须调用
retain。一旦你retain了,你就成了新的拥有者之一,你也必须负责对应的release。 -
传递(Return Values): * 如果你写的方法内部
alloc了一个对象并返回,你必须在返回前调用autorelease,将释放责任转交给 Autorelease Pool。- 这样调用方就能在当前周期内拿到存活的对象,而你又履行了释放义务。
3. 三种“持有者”角色判定
| 获取方式 | 拥有状态 | 负责动作 |
|---|---|---|
调用 alloc/new/copy | 绝对拥有 | 必须 release |
调用 [obj retain] | 主动持有 | 必须 release |
调用类工厂方法 (arrayWith...) | 临时使用 | 严禁 release |
4. 为什么这套规则如此重要?
这是因为 Objective-C 的方法调用是动态的。编译器在 MRC 下无法预知一个方法返回的对象到底是被释放了还是存活着。
苹果通过这套**命名规范(Naming Convention)**强制开发者达成共识:
- 如果我写的方法名不带
copy/alloc/new,我向你保证: “这个对象现在是安全的,你直接用,但我一会儿会把它自动收走。如果你想长久留住它,请你自己retain。”
5. MRC 判定中的“黑洞”:Setter 方法
在 MRC 编写 Setter 是最容易出错的地方,准确的规则是:先 retain 新值,再 release 旧值,最后赋值。
Objective-C
- (void)setName:(NSString *)newName {
if (_name != newName) {
[newName retain]; // 1. 获得新值所有权
[_name release]; // 2. 放弃旧值所有权
_name = newName; // 3. 赋值
}
}
如果不按这个顺序(比如先 release 再 retain),当新旧值是同一个对象时,对象可能在这一瞬间被彻底销毁,导致野指针错误。
总结
判定规则极其简单:搜关键字。
如果你在代码中写了 alloc, new, copy, 或 retain,而没有在对应的逻辑路径上写 release 或 autorelease,那你就是那个“不负责任”的开发者。