在Objective-C中,@property默認生成的實例變量(ivar)是私有的(@private)。但「私有」是否意味著「不可繼承」?本文通過實測代碼,揭示私有ivar可被子類繼承(佔據內存),但不可直接訪問,需通過父類方法間接操作的真相。
父類Fun:
@interface Fun : NSObject
{
@private NSString *_name; // 私有ivar
}
- (void)setNameWith:(NSString*)newName; // setter
- (NSString*)getName; // getter
- (void)print; // 打印類名與_name
@end
@implementation Fun
- (void)setNameWith:(NSString*)newName { _name = newName; }
- (NSString*)getName { return _name; }
- (void)print { NSLog(@"%@--->%@", NSStringFromClass([self class]), _name); }
@end
子類FunA、FunB繼承Fun,不添加新成員。
Fun *fun = [[Fun alloc] init]; FunA *funA = [[FunA alloc] init]; FunB *funB = [[FunB alloc] init]; [fun setNameWith:@"皇帝"]; [fun print]; // Fun--->皇帝 [funA print]; // FunA--->(null) [funB print]; // FunB--->(null) [funA setNameWith:@"太子A"]; [funB setNameWith:@"太子B"]; [fun print]; // Fun--->皇帝 [funA print]; // FunA--->太子A [funB print]; // FunB--->太子B
_name存儲:fun、funA、funB各自修改_name互不干擾,說明子類實例繼承了父類的私有ivar內存佈局。_name:在FunA方法中寫_name = ...會編譯報錯「ivar is private」。[super setNameWith:]或繼承的setNameWith:正常工作。@property默認即@private:@property (nonatomic, copy) NSString *name;編譯器自動合成@private NSString *_name;。這就是為什麼平時覺得「子類繼承了屬性」——實際是繼承了存儲槽位+存取方法。| 維度 | @private ivar | @property (默認) |
|---|---|---|
| 內存繼承 | 是(子類實例包含該槽位) | 是 |
直接訪問(_ivar) | 否(編譯錯誤) | 否 |
| 方法訪問(getter/setter) | 是(繼承父類方法) | 是(自動合成方法) |
| 子類重寫屬性 | 需重新聲明@property | 可直接override |
「私有不可繼承」是C++/Java的語義,在Objective-C中不成立。 OC的@private僅限制編譯期直接名字查找,不影響運行時內存佈局繼承。這反映了OC動態運行時的特性:消息發送(方法調用)比直接內存訪問更核心。
@private ivar+手寫存取器。@protected(默認)或定義@property在類擴展(Class Extension)中,頭文件不暴露。這個實驗揭示了OC對象模型的本質:繼承的是內存佈局與方法表,訪問控制僅在編譯期生效。