在早期 Objective-C 中,為每個實例變數手寫 setter/getter 既繁瑣又易出錯:
// 手動聲明
- (void)setRainHandling:(float)rainHandling;
- (float)rainHandling;
- (void)setSnowHandling:(float)snowHandling;
- (float)snowHandling;
@property 編譯指令自動生成 setter/getter 聲明,將上述四行簡化為:
@property float rainHandling;
@property float snowHandling;
聲明僅是介面,實現靠 @synthesize:
@synthesize rainHandling;
@synthesize snowHandling;
編譯器會自動生成標準的存取方法實現,並自動創建同名實例變數(若未手動聲明)。Xcode 4.5+ 起甚至可省略 @synthesize,完全由編譯器自動處理。
| 修飾符 | 語義 | 適用場景 |
|---|---|---|
assign | 簡單賦值,不保留 | 基本資料型別(int, float)、delegate 弱引用 |
retain | 保留新值,釋放舊值 | MRC 環境下的物件屬性 |
copy | 複製新物件,與原物件獨立 | NSString, NSArray, NSDictionary 等可變類別的不可變對應類 |
strong | ARC 環境下等同 retain | ARC 專案的物件屬性(預設) |
weak | 弱引用,物件釋放自動置 nil | Delegate、避免循環引用 |
readonly | 僅生成 getter | 計算屬性、不可變狀態 |
readwrite | 生成 setter 與 getter(預設) | 可讀寫屬性 |
nonatomic | 非原子操作,不加鎖,效率高 | 單線程訪問、效能敏感場景(iOS 開發常用) |
atomic | 原子操作,線程安全(預設) | 多線程共享屬性(有效能開銷) |
// 等價於 [obj setRainHandling:0.5];
obj.rainHandling = 0.5;
// 等價於 [obj rainHandling];
float value = obj.rainHandling;
點表達式本質仍是呼叫 setter/getter,非直接訪問實例變數。左值調用 setter,右值調用 getter。
@interface AllWeatherTire : Tire {
NSString *tireName; // 實例變數名不同
}
@property (copy) NSString *name; // 公開屬性名
@end
@implementation AllWeatherTire
@synthesize name = tireName; // 映射關係
// 內部必須用 self.name = @"Car",不能直接用 name = @"Car"
@end
@property (readonly) float bodyMassIndex;
@dynamic bodyMassIndex; // 不生成實例變數、不生成存取方法
- (float)bodyMassIndex {
return self.weight / (self.height * self.height); // 動態計算
}
若調用不存在的 setter/getter 會崩潰。適用於 Core Data 托管屬性、動態代理等場景。
屬性僅支援單參數 setter/getter,無法處理帶額外參數的方法,例如:
- (void)setTire:(Tire *)tire atIndex:(int)index;
- (Tire *)tireAtIndex:(int)index;
此類場景仍需手動實現方法。
@property:聲明屬性特徵(類型、記憶體語義、線程安全)@synthesize:請求編譯器生成標準實現與實例變數@dynamic:完全手動實現,編譯器不介入copy 防御可變物件副作用,weak 打破循環引用