2-1.【OC】【内存管理】MRC 下,“谁拥有对象?谁负责释放?”的准确判定规则是什么?

0 阅读3分钟

在 MRC(Manual Reference Counting)时代,判定“谁负责释放”并不看心情,而是遵循一套极其严苛的、基于方法名关键字黄金法则

这套规则由苹果定义,本质上是把 C 语言对内存管理的模糊性,转化为了一套语义契约


1. 黄金法则:看方法名开头

在 MRC 下,你是否“拥有”一个对象,完全取决于你获取该对象时调用的方法名。

规则 A:你拥有对象(需要你手动 releaseautorelease

如果你通过以下关键字开头的方法创建/获取对象,你就获得了该对象的所有权

  • 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,而没有在对应的逻辑路径上写 releaseautorelease,那你就是那个“不负责任”的开发者。