在 ARC 普及前,Objective-C 開發者必須手動管理內存。理解引用計數、所有權轉移、自動釋放池機制,不僅有助於維護舊代碼,更能深入掌握 ARC 底層語義,編寫高性能、無洩漏的現代代碼。本文從對象圖視角重構內存管理心智模型,系統講解 retain/release/autorelease、屬性修飾符、特殊對象行為、循環引用破解。
將整個程序視為對象嵌套樹:
// 創建即擁有 (+1) id obj = [[MyClass alloc] init]; // retainCount = 1 id obj2 = [obj copy]; // retainCount = 1 (新對象) id obj3 = [array objectAtIndex:0]; // 無所有權 (autoreleased) // 獲取所有權 (+1) [obj retain]; // 放棄所有權 (-1) [obj release]; // retainCount 歸零 → dealloc // 延遲釋放 (歸入最近的 autorelease pool) [obj autorelease]; // 當前 pool drain 時 release
UIView *sub = [[UIView alloc] initWithFrame:rect]; [self.view addSubview:sub]; // self.view retain sub (+1) [sub release]; // 放棄我方所有權,sub 仍由 self.view 持有 // 當 sub 從 superview 移除,self.view release → sub 銷毀
// .h
@property (nonatomic, retain) UIView *contentView;
// .m
@synthesize contentView = _contentView;
// setter 語義 (retain)
- (void)setContentView:(UIView *)v {
if (v != _contentView) {
[v retain]; // 新值 +1
[_contentView release]; // 舊值 -1
_contentView = v;
}
}
// dealloc 釋放
- (void)dealloc {
[_contentView release];
[super dealloc];
}
| 修飾符 | setter 行為 | 適用場景 |
|---|---|---|
retain | [new retain]; [old release]; _prop = new; | 對象類型,宿主持有所有權 |
copy | [new copy]; [old release]; _prop = new; | 不可變類型 (NSString/NSArray/NSDictionary),防外部修改 |
assign | _prop = new; (無 retain/release) | 基本類型、標量、弱引用 (委托 delegate) |
陷阱:assign 用於對象極易產生懸空指針 (對象被釋放後指針未置 nil)。現代代碼請用 weak (ARC) 或手動置 nil。
autorelease 將對象放入最近的池release- (NSString *)createString {
NSString *s = [[NSString alloc] init];
return [s autorelease]; // 調用者可直接用,無需 release
}
// 手動創建池 (舊式線程/大量臨時對象)
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// ... 大量 autorelease 對象 ...
[pool drain]; // 批量釋放
[array addObject:obj] → retain;移除/清空 → releaseretain (ARC 自動處理,非 ARC 需 Block_copy)retain target;invalidate 前不釋放@"text" 駐留在數據段,無需管理sharedInstance 永不釋放模式:A retain B,B retain A (如 ViewController ↔ Delegate, Block 捕獲 self)
// 非 ARC 手動實現弱引用 @property (assign) id delegate; // 不 retain // 使用時需確保 delegate 存活 (外部強引用持有) // ARC 直接用 @property (weak) id delegate;
__weak + __strong__weak typeof(self) weakSelf = self;
void (^blk)(void) = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf) { [strongSelf doSomething]; }
};
__block 手動置 nil (舊代碼)__block typeof(self) tmp = self;
void (^blk)(void) = ^{
[tmp doSomething];
tmp = nil; // 必須執行到此處才能打破環
};
NSZombieEnabled=YES,釋放後對象轉為僵屍,再次發送消息報錯而非崩潰static analyzer (Clang):編譯期靜態檢測 retain/release 不匹配手動內存管理的本質是所有權傳遞協議。牢記三條鐵律:
理解這些機制後,閱讀 ARC 生成的代碼 (clang -rewrite-objc) 將變得清晰可預期,也能在性能敏感場景精準控制對象生命週期。