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

Objective-C 繼承機制深度解析:@property 默認屬性、私有成員與子類訪問真相

     編輯:關於C

現象:直覺與現實的衝突

日常開發中常寫:

@property (nonatomic, copy) NSString *name;

子類能直接用 self.name、_name 嗎?@property 默認生成的實例變量是什麼訪問級別?

實驗驗證:私有成員是否可繼承?

// Father.h
@interface Father : NSObject
@private
    NSString *_name;  // 顯式私有
- (void)setName:(NSString *)n;
- (NSString *)name;
- (void)print;
@end

// Father.m
@implementation Father
- (void)setName:(NSString *)n { _name = n; }
- (NSString *)name { return _name; }
- (void)print { NSLog(@"%@ ---> %@", NSStringFromClass([self class]), _name); }
@end

// ChildA.m / ChildB.m 繼承 Father

// main.m
int main() {
    Father *f = [Father new];
    ChildA *a = [ChildA new];
    ChildB *b = [ChildB new];

    [f setName:@"皇帝"];
    [f print];  // Father ---> 皇帝
    [a print];  // ChildA ---> (null)
    [b print];  // ChildB ---> (null)

    [a setName:@"太子A"];
    [b setName:@"太子B"];
    [f print];  // Father ---> 皇帝
    [a print];  // ChildA ---> 太子A
    [b print];  // ChildB ---> 太子B
}

結果分析

調用輸出說明
[f setName:@"皇帝"]Father: 皇帝父類實例變量賦值
[a print]ChildA: (null)子類實例變量獨立,未共享父類內存
[a setName:@"太子A"]ChildA: 太子A子類調用繼承方法,操作自己的 _name
[f print]Father: 皇帝父類實例變量未受影響

核心結論

  1. @private 實例變量會被繼承:子類對象內存包含父類所有實例變量(包括私有)
  2. 但直接訪問被編譯器禁止:子類方法中寫 _name 編譯報錯
  3. 通過繼承的公共方法(setter/getter)可間接訪問:操作的是子類自己的 那份內存副本
  4. 每個實例擁有完整的父類實例變量集合,互不干擾

@property 默認行為

  • 現代 Clang (LLVM 3.1+) 自動合成實例變量 _propertyName
  • 默認私有(@private),但可通過 @synthesize name = _name; 指定
  • 子類可通過 self.name (調用繼承的 getter/setter) 訪問自己的 那份存儲

最佳實踐建議

  1. 封裝性優先:不要在子類直接訪問父類實例變量,統一用屬性/方法
  2. 需共享狀態時:在父類提供 protected 方法或屬性,明確語義
  3. 避免 @public 實例變量,破壞封裝
  4. 理解 @synthesize 與自動合成的區別,統一團隊風格

總結

Objective-C 中 私有實例變量會被繼承(內存佈局包含),但直接訪問受編譯器限制。子類通過繼承方法操作的是各自副本。這種設計兼顧了內存佈局兼容性與封裝性,是理解 OC 物件模型的關鍵。

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