Objective-C 中「成員變量(Instance Variable,ivar)」與「屬性」是初學者最易混淆的概念。本文通過實例代碼演示,得出核心結論:類內部使用成員變量({} 中聲明),類外部使用屬性(@property),並詳細講解訪問控制、手動 Getter/Setter、@synthesize 自動合成、現代 Runtime 自動合成的演進。
// People.h
@interface People : NSObject {
NSString *_peopleName; // 默認 @protected
}
// @public 可改為公開,但不推薦
@end
obj->ivar(需 @public 或同類/子類內部)。@protected:僅當前類與子類可見。// People.m
@implementation People
- (instancetype)init {
self = [super init];
if (self) {
_peopleName = @"people name 1"; // 直接賦值 ivar
}
return self;
}
- (NSString *)getName { return _peopleName; }
- (void)setName:(NSString *)name { _peopleName = name; }
@end
類內部直接操作 _ivar 高效且無副作用,是標準做法。
@property (nonatomic, strong) NSString *peopleName; // nonatomic: 非原子操作,多線程不加鎖,性能高 // strong: 強引用(ARC 下相當於 retain),對象持有期管理
@implementation People
@synthesize peopleName = _peopleName; // 將屬性 peopleName 映射到 ivar _peopleName
// 自動生成 getter/setter:
// - (NSString *)peopleName { return _peopleName; }
// - (void)setPeopleName:(NSString *)name { _peopleName = name; }
@end
_ 關鍵:區分屬性名與 ivar 名,避免無限遞歸。@synthesize,編譯器自動生成 _propertyName ivar 及存取方法。People *p = [[People alloc] init]; NSLog(@"%@", p.peopleName); // 呼叫 getter p.peopleName = @"new name"; // 呼叫 setter
點語法 p.peopleName 編譯器自動轉為 [p peopleName] / [p setPeopleName:]。
| 場景 | 推薦做法 | 原因 |
|---|---|---|
| .h 介面聲明 | 只寫 @property,不寫 {} ivar | 編譯器自動生成私有 ivar,介面乾淨 |
| .m 實現初始化 | 直接用 _propertyName 賦值 | 繞過 setter 副作用,效率高 |
| 類內部邏輯讀寫 | 優先用 _ivar,需觸發 KVO/副作用時用 self.property | 區分「內部狀態變更」與「公開介面契約」 |
| 類外部訪問 | 只用 self.property / obj.property | 享受封裝、KVO、內存管理、線程安全(atomic) |