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

Objective-C @property 與 @synthesize 深度解析:從手動 MRC 到現代自動合成的演進

     編輯:關於C語言

歷史演進三階段

階段Xcode/Clang 版本特徵開發者工作
MRC 手動時代Xcode < 4.4需手寫 @synthesize、dealloc、retain/release聲明 ivar、寫 setter/getter、內存管理
@synthesize 時代Xcode 4.4~@property 自動生成聲明,@synthesize 自動生成實現 + ivar寫 @property + @synthesize,ARC 管理內存
自動合成時代 (現代)Xcode 4.5+ (LLVM 4.0+)@property **默認自動**合成 ivar、setter、getter,**無需 @synthesize**僅寫 @property,專注業務邏輯

@property 屬性修飾符完整速查

@property (attributes) Type name;
屬性含義適用類型默認值
原子性
atomic線程安全 setter/getter (鎖/自旋鎖)所有對象類型✅ 默認
nonatomic非線程安全,高性能所有類型iOS 推薦
內存管理 (對象類型)
strong持有強引用 (ARC 保留)對象指針✅ 默認
weak弱引用,對象釋放自動置 nil對象指針打破循環引用
copy複製一份 (不可變副本)NSString, NSArray, NSDictionary, Block防止外部修改
assign直接賦值,不管理內存基本類型, 非對象指針基本類型默認
unsafe_unretained弱引用但不置 nil (危險)對象指針極少用
讀寫權限
readwrite生成 setter + getter✅ 默認
readonly僅生成 getter不可變屬性
方法重命名
getter=name自定義 getter 名稱BOOL 類型常用 isEnabled屬性名
setter=name:自定義 setter 名稱setPropertyName:

@synthesize 手動控制 (歷史/特殊需求)

// 現代默認:@property 自動生成 _ivar (下劃線前綴)
// 手動指定 ivar 名稱 (舊代碼遷移/特殊命名)
@synthesize name = _name;    // 綁定到現有 _name
@synthesize age = _age;      // 若 _age 不存在,自動創建 @private ivar

// 手動實現 setter/getter 任一,編譯器不再自動生成另一側
// 若同時手動實現 setter+getter,編譯器**不**自動創建 ivar (需自行聲明)

現代最佳實踐 (Xcode 12+ / iOS 13+)

1. 接口聲明 (.h)

@interface User : NSObject

// 推薦:nonatomic + 強/弱/複製語義明確
@property (nonatomic, copy) NSString *username;
@property (nonatomic, strong) NSString *email;
@property (nonatomic, assign) NSInteger age;      // 基本類型用 assign
@property (nonatomic, getter=isActive) BOOL active; // BOOL 推薦 is 前綴
@property (nonatomic, weak) id delegate; // 代理必須 weak

// 唯讀屬性 (初始化後不可變)
@property (nonatomic, readonly, copy) NSString *uuid;

@end

2. 實現 (.m)

@implementation User

// 無需 @synthesize,編譯器自動:
// @synthesize username = _username;
// @synthesize email = _email;
// ...

// 自定義初始化器設置 readonly 屬性
- (instancetype)initWithUsername:(NSString *)username email:(NSString *)email {
    self = [super init];
    if (self) {
        _username = [username copy]; // 直接訪問 ivar
        _email = [email copy];
        _uuid = [[NSUUID UUID] UUIDString];
    }
    return self;
}

// 覆寫 setter (如需副作用)
- (void)setActive:(BOOL)active {
    if (_active != active) {
        _active = active;
        [self updateUIForActiveState];
    }
}

// 覆寫 getter (懶加載/計算屬性)
- (NSString *)displayName {
    return _username.length ? _username : @"Guest";
}

@end

ivar 訪問規範:直接訪問 vs 存取器

場景推薦方式理由
init / dealloc直接訪問 _ivar避免副作用、KVO 觸發、未完全構造時調用覆寫方法
性能極度敏感熱路徑直接訪問 _ivar省去方法調用開銷 (極少見)
其他所有情況存取器 self.prop / [self setProp:]觸發 KVO、繼承覆寫、副作用、原子性保證、調試斷點

常見坑與避坑指南

  1. copy vs strong:NSString *、NSArray *、NSDictionary *、Block **必須用 copy**,防止可變對象外部修改導致內部狀態不可預期變化。
  2. 循環引用:父→子 strong,子→父 **weak** (delegate, block 捕獲 self 用 __weak typeof(self) weakSelf = self;)。
  3. readonly 欺騙:外部不可寫,內部可通過 _ivar 或類擴展 @property (readwrite) 修改。
  4. atomic 性能陷阱:iOS 上 默認 atomic 但極慢**,**永遠顯式寫 nonatomic**,除非真正需要線程安全 (極少見)。
  5. Block 屬性:@property (nonatomic, copy) void (^completion)(BOOL); Block 必須 copy 從棄移至堆。

屬性聲明的編譯器生成內容 (現代自動合成)

@property (nonatomic, strong) NSString *name;

// 編譯器自動生成等價於:
// 1. 實例變量 (ivar)
@private
NSString *_name;

// 2. Getter
- (NSString *)name {
    return _name;
}

// 3. Setter
- (void)setName:(NSString *)name {
    if (_name != name) {
        [_name release]; // MRC 時代
        _name = [name retain]; // MRC
        // ARC 時代自動插入 retain/release
    }
}

// 4. 類擴展中可重新聲明改變屬性
@interface User ()
@property (readwrite) NSString *uuid; // 擴展為 readwrite
@end

總結

從手動 @synthesize 到完全自動合成,Objective-C 屬性機制經歷了「減樣板、增安全、重約定」的演進。現代開發只需記住:聲明 @property 並明確非原子/內存語義、init/dealloc 訪問 ivar、其餘用 self.、delegate/block 用 weak/copy、readonly 內部可寫。理解底層生成機制有助於調試、性能分析與舊代碼維護,但日常開發請擁抱自動合成,專注於業務邏輯。

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