首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Objective-C 手動內存管理(MRC)核心機制與屬性屬性詳解

     編輯:關於C語言

雖然 ARC(自動引用計數)已成主流,但理解 MRC(手動引用計數)仍是掌握 Objective-C 內存模型、排查疑難雜症、閱讀舊代碼庫的基石。本文系統梳理引用計數規則、釋放池機制、@property 屬性修飾符及其背後的存取方法實現。

一、引用計數核心規則

操作方法計數變化備註
創建/拷貝alloc / new / copy / mutableCopy+1你擁有該對象,需負責釋放
保持retain+1聲明擁有權
釋放release-1放棄擁有權,計數歸零時調用 dealloc
自動釋放autorelease延遲 -1放入當前 NSAutoreleasePool,池排空時釋放

核心原則(Cocoa 內存管理規範):

  • 你 alloc/copy 創建的對象,你負責 release。
  • 你 retain 的對象,你負責 release。
  • 不要釋放你不擁有的對象。

二、NSAutoreleasePool 自動釋放池

釋放池是 autorelease 對象的容器。池排空(drain / release)時,向池內所有對象發送 release。

// 經典 MRC main 函數範本
int main(int argc, const char *argv[]) {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    
    // 業務代碼...
    // 便利構造器返回的對象已 autorelease
    NSArray *arr = [NSArray arrayWithObjects:@"A", @"B", nil];  
    
    [pool release];  // 或 [pool drain],排空池並釋放池本身
    return 0;
}

注意:autorelease 不會立即改變引用計數,只是在池中登記。若在池排空前對象被 retain,則計數不會歸零。

三、@property 屬性修飾符全景

@property (屬性列表) 類型 名稱;

1. 讀寫權限

  • readwrite(預設):生成 getter + setter。
  • readonly:僅生成 getter。

2. 原子性

  • atomic(預設):setter/getter 以鎖保護,線程安全但有性能開銷。
  • nonatomic:無鎖,高性能,iOS 開發慣用。

3. 內存管理語義(重點)

修飾符適用類型Setter 邏輯示意典型場景
assign基本類型、NSInteger、CGRect 等純量_ivar = newValue;非對象類型;弱引用(易產生懸空指針)
retain / strongNSObject 子類[newValue retain]; [_ivar release]; _ivar = newValue;擁有關係,父持有子、單例持有
copy遵守 NSCopying 的對象(NSString、NSArray、NSDictionary、Block)_ivar = [newValue copy];需不可變快照,防止外部修改影響內部狀態

Setter 實現細節對比

// retain 語義
- (void)setName:(NSString *)newName {
    if (newName != _name) {
        [_name release];          // 先釋放舊值(安全:對 nil 發送 release 無害)
        _name = [newName retain]; // 再保持新值
    }
}

// copy 語義
- (void)setTitle:(NSString *)newTitle {
    if (newTitle != _title) {
        [_title release];
        _title = [newTitle copy]; // 複製,新對象 retainCount = 1
    }
}

四、自定義類無法直接用 copy

只有遵守 NSCopying 協議並實現 -copyWithZone: 的類才能配合 copy 屬性。對於自定義模型類,若需複製語義,必須實現該協議;否則請改用 strong 或提供專門的 copyInstance 方法。

五、assign / retain / copy 內存圖解

假設 NSString *str = [[NSString alloc] initWithString:@"hello"]; 堆地址 0x1111,棧指針 0x2222 指向 0x1111。

  • assign:newStr = str; 兩指針同指 0x1111,計數不變。釋放任一導致另一懸空。
  • retain:newStr = [str retain]; 新指針 0x4444 指向 0x1111,計數 +1。雙方各自 release 才安全。
  • copy:newStr = [str copy]; 新建對象 0x3333 內容「hello」,計數 = 1。互不干擾。

六、實戰代碼片段

// Song.h
@interface Song : NSObject
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *artist;
@property (nonatomic, assign) long long duration;
- (instancetype)initWithTitle:(NSString *)t artist:(NSString *)a duration:(long long)d;
@end

// Song.m
@implementation Song
@synthesize title = _title, artist = _artist, duration = _duration;

- (instancetype)initWithTitle:(NSString *)t artist:(NSString *)a duration:(long long)d {
    if ((self = [super init])) {
        self.title = t;      // 呼叫 copy setter
        self.artist = a;
        self.duration = d;
    }
    return self;
}

- (void)dealloc {
    [_title release];
    [_artist release];
    [super dealloc];
}
@end

總結

MRC 時代的內存管理規則嚴謹而對稱:誰持有,誰釋放。@property 的修飾符將這套規則編碼為聲明式語法,編譯器自動生成符合規範的存取方法。理解其背後的 retain/release/copy 邏輯,不僅能駕馭舊代碼,更能在 ARC 環境下精準控制對象圖生命週期、解決循環引用與性能瓶頸。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved