MRC 三大核心方法
| 方法 | 作用 | 引用計數變化 |
retain | 聲明擁有權 | +1 |
release | 放棄擁有權 | -1 |
autorelease | 延遲釋放(下一個自動釋放池清空時) | 延遲 -1 |
copy | 創建副本(新對象) | 新對象計數 = 1 |
屬性修飾符對應的 Setter 語義
1. retain / strong(ARC 術語)
// MRC 手寫 setter 等價邏輯
- (void)setNames:(NSMutableArray *)names {
if (_names != names) {
[_names release]; // 1. 釋放舊值
_names = [names retain]; // 2. 保留新值
}
}
// dealloc 時需 [_names release] 平衡
2. assign / weak
- (void)setAge:(int)age {
_age = age; // 純賦值,無 retain/release
}
// 適用:基本數據類型、delegate 避免循環引用
3. copy
- (void)setNames:(NSMutableArray *)names {
_names = [names copy]; // 創建不可變副本
}
// 必須用於:NSString、NSArray、NSDictionary 等不可變類
// 原因:防止外部修改可變對象導致內部狀態意外改變
4. readonly
- 僅生成 getter,無 setter
- 常用於初始化後不可變的屬性
5. nonatomic vs atomic
| 特性 | nonatomic | atomic |
|------|-----------|--------|
| 線程安全 | 否 | 是(鎖保護) |
| 效能 | 高 | 低 |
| 適用場景 | 單線程、內部數據、效能敏感 | 多線程共享數據 |
| 預設值 | 否 | **是**(歷史原因) |
類型對應修飾符速查表
| 數據類型 | 推薦修飾符 | 理由 |
|----------|------------|------|
| 基本類型 (int, float, BOOL...) | `assign` / `weak` | 無引用計數,預設即可 |
| **NSString** | **`copy`** | 防止可變副本修改鏈式反應 |
| 其他不可變對象 (NSArray, NSDictionary...) | `copy` | 同上 |
| 可變對象 (NSMutableArray...) | `strong` / `retain` | 通常需保持同一對象引用 |
| Delegate / Block | `weak` | 避免循環引用 |
| IBOutlet | `weak` (iOS) / `strong` (macOS) | 視圖層級所有權語義 |
記憶體管理黃金法則
- 誰創建,誰釋放:`alloc`/`new`/`copy`/`mutableCopy` → 你擁有 → 需 `release`/`autorelease`
- 取得非你創建的對象:若需長期持有 → `retain`;用完 → `release`
- Setter 規範:`retain`/`copy` 舊值 release → 新值 retain/copy
- Dealloc 清理:所有 `retain`/`copy` 持有的屬性逐一 `release`
- Autorelease 池:`@autoreleasepool { ... }` 限制作用域,避免臨時對象堆積
常見崩潰場景與修正
| 場景 | 錯誤代碼 | 修正 |
|------|----------|------|
| 過度釋放 | `[obj release]; [obj release];` | 僅釋放一次,或改用 `autorelease` |
| 野指針訪問 | `release` 後繼續用 `obj` | `release` 後置 `nil`:`[obj release]; obj = nil;` |
| 循環引用 | A.retain(B), B.retain(A) | 一邊用 `weak`/`assign`(通常 delegate/block) |
| 可變對象外洩 | `@property (retain) NSMutableArray *arr;` | 改 `@property (copy) NSArray *arr;` 並內部用可變版本操作 |
從 MRC 遷移到 ARC 核心心智轉變
- **不再手寫** `retain`/`release`/`autorelease`/`dealloc`(除非管理 Core Foundation/C++ 對象)
- **屬性修飾符映射**:`retain` → `strong`,`assign` → `weak`/`unsafe_unretained`,`copy` 保持
- **編譯器插入** 的釋放代碼基於所有權修飾符與作用域分析
- **循環引用** 仍需開發者用 `weak` 斷開
總結
MRC 時代的記憶體管理雖繁瑣,但強迫開發者建立所有權、生命週期、引用計數的精確心智模型。理解這些本質,對於除錯 ARC 時代的循環引用、過早釋放、Core Foundation 橋接等疑難雜症依然至關重要。