【iOS】繼承鏈

文章目錄

  • 前言
  • 什么是繼承鏈
  • OC中的根類
    • 關于NSProxy
  • 關鍵作用
    • 1.方法查找與動態綁定
    • 2. 消息轉發
    • 3. **類型判斷與多態**
  • 繼承鏈的底層實現
    • 元類的繼承鏈
  • 總結

前言

??在objective-c中,繼承鏈是類與類之間通過父類(Superclass)關系形成的一層層繼承結構,在我們之前的學習中,我們發現無論是方法的動態查找、消息的傳遞還是代碼的復用,都需要用到繼承鏈,今天我們就來系統地了解一下繼承鏈。

什么是繼承鏈

??每個OC類(除根類外)都有一個直接父類,通過這種層級關系形成一條從子類到根類的單向鏈。當向一個對象發送消息(調用方法)時,OC運行時會沿著這條鏈自底向上查找對應的方法實現:

  • 若當前類實現了該方法,直接調用;
  • 若未實現,則跳轉到父類繼續查找;
  • 直到根類仍無實現時,觸發「消息轉發」機制(否則程序崩潰)。

OC中的根類

??OC 中幾乎所有類的最終父類都是 NSObject(少數特殊類如 NSProxy可能作為獨立根類)。NSObject定義了OC對象的基礎行為(如內存管理、反射、消息傳遞等)。

eg:

// 自定義類繼承鏈示例
@interface MyBaseClass : NSObject
@end@interface MySubClass : MyBaseClass
@end// MySubClass 的繼承鏈:MySubClass → MyBaseClass → NSObject → nil(根類無父類)

關于NSProxy

??在 Objective-C(OC)中,NSProxy是一個特殊的抽象基類,與 NSObject并列作為 OC 類體系的兩大根類(但 NSObject是絕大多數類的最終父類,而 NSProxy更專注于消息轉發場景)。它的核心設計目標是==輕量級消息轉發,常用于實現代理模式(Proxy Pattern)、動態消息攔截或替代復雜的繼承結構。

NSProxy的底層源碼如下:

/*	NSProxy.hCopyright (c) 1994-2019, Apple Inc. All rights reserved.
*/#import <Foundation/NSObject.h>@class NSMethodSignature, NSInvocation;NS_HEADER_AUDIT_BEGIN(nullability, sendability)NS_ROOT_CLASS
@interface NSProxy <NSObject> {__ptrauth_objc_isa_pointer Class	isa;
}+ (id)alloc;
+ (id)allocWithZone:(nullable NSZone *)zone NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
+ (Class)class;- (void)forwardInvocation:(NSInvocation *)invocation;
- (nullable NSMethodSignature *)methodSignatureForSelector:(SEL)sel NS_SWIFT_UNAVAILABLE("NSInvocation and related APIs not available");
- (void)dealloc;
- (void)finalize;
@property (readonly, copy) NSString *description;
@property (readonly, copy) NSString *debugDescription;
+ (BOOL)respondsToSelector:(SEL)aSelector;- (BOOL)allowsWeakReference API_UNAVAILABLE(macos, ios, watchos, tvos);
- (BOOL)retainWeakReference API_UNAVAILABLE(macos, ios, watchos, tvos);// - (id)forwardingTargetForSelector:(SEL)aSelector;@endNS_HEADER_AUDIT_END(nullability, sendability)

通過上述代碼,我們可以發現NSProxy是一個實現了NSObject協議的根類。

在這里插入圖片描述
由此我們知道:

  • NSProxy 是一個抽象類,跟 NSObject 一樣的基類,都遵守NSObject協議

  • NSProxy是一個抽象類,必須繼承實例化其子類才能使用。

  • NSProxy從類名來看是代理類專門負責代理對象轉發消息的。相比NSObject類來說NSProxy更輕量級,OC是單繼承的語言,但通過NSProxy可以幫助Objective-C間接的實現多重繼承的功能(“偽多繼承”)。

在NSProxy源碼中,運用消息轉發機制的核心方法有兩個:

在這里插入圖片描述

通過methodSignatureForSelector:方法獲取一個NSMethodSignature類型的對象,調用forwardInvocation:方法。該方法傳入一個封裝了NSMethodSignatureNSInvocation對象。然后該對象通過invakeWithTarget:方法將消息轉發給其它對象。

順便回顧一下消息轉發:

OC的消息轉發流程:當向一個對象發送消息,而該對象沒有實現對應的方法時,運行時會觸發消息轉發機制。這個過程分為幾個步驟:動態方法解析、備用接收者轉發,最后是完整消息轉發。這兩個方法主要參與最后一步,即完整消息轉發階段。

methodSignatureForSelector:的作用是為指定的選擇器生成方法簽名。方法簽名包含了方法的參數類型、返回類型等信息,是構造NSInvocation對象所必需的。如果這個方法返回nil,運行時會認為該消息無法處理,進而觸發doesNotRecognizeSelector:導致崩潰。

然后是forwardInvocation:,它的作用是處理那些無法被當前對象或其繼承鏈中其他類處理的方法調用。在這個方法里,我們可以自定義如何處理這些未被識別的消息,比如將消息轉發給其他對象,或者執行一些額外的邏輯。

當消息轉發到NSProxy或自定義類時,首先會調用methodSignatureForSelector:來獲取方法簽名,如果返回有效的簽名,才會調用forwardInvocation:來處理消息。如果methodSignatureForSelector:返回nil,則不會調用forwardInvocation:,直接崩潰。

關鍵作用

1.方法查找與動態綁定

OC 是動態語言,方法的調用(消息發送)發生在運行時。繼承鏈的存在使得對象可以「繼承」父類的方法,無需重復實現。例如:

MySubClass *obj = [[MySubClass alloc] init];
NSString *desc = [obj description]; // 實際調用 NSObject 的 description 方法

即使 MySubClass未重寫 description,運行時仍會沿繼承鏈找到 NSObject的實現。

2. 消息轉發

若繼承鏈中所有類都未實現目標方法,OC 會嘗試通過消息轉發機制處理,避免直接崩潰。典型流程如下:

  1. 動態方法解析:調用 +resolveInstanceMethod:(實例方法)或 +resolveClassMethod:(類方法)嘗試動態添加方法實現;
  2. 備用接收者轉發:調用 -forwardingTargetForSelector:將消息轉發給其他對象;
  3. 完整消息轉發:調用 -methodSignatureForSelector:生成方法簽名,再通過 -forwardInvocation:轉發(可自定義處理邏輯)。

3. 類型判斷與多態

通過繼承鏈可以實現多態(Polymorphism)。例如,isKindOfClass:isMemberOfClass:方法通過檢查對象繼承鏈判斷類型:

id obj = [[MySubClass alloc] init];
BOOL isMyBase = [obj isKindOfClass:[MyBaseClass class]]; // YES(繼承鏈包含 MyBaseClass)
BOOL isNSObject = [obj isKindOfClass:[NSObject class]];   // YES(繼承鏈最終到 NSObject)

繼承鏈的底層實現

OC 類的底層通過 objc_class結構體表示,其中 superclass字段指向父類。通過運行時函數可手動操作繼承鏈:

  • class_getSuperclass(Class cls):獲取類的直接父類;
  • class_getClass(Class cls):獲取類對應的元類(Meta Class);
  • object_getClass(id obj):獲取對象的類(等價于 [obj class])。

元類的繼承鏈

OC 中類(Class)本身也是對象,其類型為元類(Meta Class)。元類的繼承鏈與普通類不同:

  • 普通類的 isa指針指向其元類;
  • 元類的 isa指針指向根元類(通常是 NSObject元類的父類);
  • 根元類的 isa指針指向自身(形成閉環)。

例如,NSObject類的元類繼承鏈為:NSObject_Meta → Root_Meta(自身)

總結

  1. 在編程設計時,我們要避免繼承鏈過長,過深的繼承鏈會增加方法查找時間,降低性能。推薦優先使用組合(Composition)而非繼承。
  2. 在繼承中,根類(如 NSObject)需自行實現部分基礎方法(如 allocinit),否則其子類無法正常使用。
  3. 類方法存儲在元類中,其繼承鏈為「元類 → 父元類 → … → 根元類」;實例方法的繼承鏈為「類 → 父類 → … → 根類」。

在面向對象編程(OOP)中,組合(Composition) 是一種通過「持有其他對象實例」來實現功能復用的設計模式,與「繼承(Inheritance)」共同構成代碼復用的兩大核心手段。它的核心思想是「整體-部分」(Whole-Part)關系,即一個對象(整體)由多個其他對象(部分)組成,通過調用這些「部分」對象的方法來實現自身功能。

在這里插入圖片描述

在面向對象編程(OOP)中,組合(Composition) 是一種通過「持有其他對象實例」來實現功能復用的設計模式,與「繼承(Inheritance)」共同構成代碼復用的兩大核心手段。它的核心思想是「整體-部分」(Whole-Part)關系,即一個對象(整體)由多個其他對象(部分)組成,通過調用這些「部分」對象的方法來實現自身功能。

組合的本質是「has-a」關系(擁有關系),而非繼承的「is-a」關系(是一種關系)。例如:

一輛 Car(整體)「擁有」一個 Engine(部分),因此 Car通過持有 Engine實例來調用 startstop等方法;

一個 Computer(整體)「擁有」一個 CPU和一個 Memory(部分),通過調用它們的計算和存儲方法完成功能。

與繼承相比,組合不要求整體類與部分類存在繼承層級,而是通過動態的消息傳遞(調用部分對象的方法)實現功能復用

維度組合(Composition)繼承(Inheritance)
關系類型「has-a」(整體擁有部分)「is-a」(子類是一種父類)
耦合度低:整體與部分通過接口(協議)交互,解耦性強高:子類依賴父類的實現細節(如私有方法、屬性)
靈活性高:運行時可動態替換部分對象(如依賴注入)低:繼承關系編譯時確定,無法動態修改
復用粒度細粒度:僅復用需要的部分功能粗粒度:必須繼承整個父類的功能(包括不需要的)
設計復雜度需定義清晰的接口(協議),規范部分對象的行為需維護繼承鏈,可能導致「脆弱基類」問題

組合的具體使用:

在 Objective-C 中,組合通過「屬性持有其他對象實例」實現。

1.定義部分對象(Component)

首先定義需要被組合的功能模塊(部分),通常通過協議(Protocol)規范其行為,以降低耦合。

// 定義 Engine 協議(部分對象的行為規范)
@protocol Engine <NSObject>
- (void)start;
- (void)stop;
- (NSString *)engineInfo;
@end// 具體實現:GasEngine(汽油發動機)
@interface GasEngine : NSObject <Engine>
@end@implementation GasEngine
- (void)start { NSLog(@"汽油發動機啟動..."); }
- (void)stop { NSLog(@"汽油發動機停止..."); }
- (NSString *)engineInfo { return @"Gas Engine v1.0"; }
@end

2. 定義整體對象(Composite)

整體對象通過屬性持有部分對象的實例,并在需要時調用其方法。

// 整體對象:Car(汽車)
@interface Car : NSObject
@property (nonatomic, strong) id<Engine> engine; // 持有符合 Engine 協議的對象
- (instancetype)initWithEngine:(id<Engine>)engine;
- (void)startCar;
- (void)stopCar;
@end@implementation Car
- (instancetype)initWithEngine:(id<Engine>)engine {if (self = [super init]) {_engine = engine; // 注入部分對象(依賴注入)}return self;
}// 調用部分對象的方法實現自身功能
- (void)startCar {[self.engine start];NSLog(@"汽車啟動,使用發動機:%@", [self.engine engineInfo]);
}- (void)stopCar {[self.engine stop];NSLog(@"汽車停止");
}
@end

3. 使用組合

通過創建部分對象的實例,并注入到整體對象中,即可完成功能復用。

// 創建部分對象(GasEngine)
id<Engine> engine = [[GasEngine alloc] init];// 創建整體對象(Car)并注入部分對象
Car *car = [[Car alloc] initWithEngine:engine];// 調用整體對象的方法(內部調用部分對象的方法)
[car startCar]; 
// 輸出:
// 汽油發動機啟動...
// 汽車啟動,使用發動機:Gas Engine v1.0

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/89786.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/89786.shtml
英文地址,請注明出處:http://en.pswp.cn/web/89786.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

論文閱讀:Instruct BLIP (2023.5)

文章目錄InstructBLIP&#xff1a;邁向通用視覺語言模型的指令微調研究總結一、研究背景與目標二、核心方法數據構建與劃分模型架構訓練策略三、實驗結果零樣本性能消融實驗下游任務微調定性分析可視化結果展示四、結論與貢獻InstructBLIP&#xff1a;邁向通用視覺語言模型的指…

Elasticsearch+Logstash+Filebeat+Kibana部署【7.1.1版本】

目錄 一、準備階段 二、實驗階段 1.配置kibana主機 2.配置elasticsearch主機 3.配置logstash主機 4.配置/etc/filebeat/filebeat.yml 三、驗證 1.開啟Filebeat 2.在logstash查看 3.瀏覽器訪問kibana 一、準備階段 1.準備四臺主機kibana、es、logstash、filebeat 2.在…

Vue開發前端報錯:‘vue-cli-service‘ 不是內部或外部命令解決方案

1.Bug: 最近調試一個現有的Vue前端代碼&#xff0c;發現如下錯誤&#xff1a; vue-cli-service’ 不是內部或外部命令&#xff0c;也不是可運行的程序 或批處理文件。 2.Bug原因&#xff1a; 導入的工程缺少依賴包&#xff1a;即缺少node_modules文件夾 3.解決方案&#xff1…

AI生態,釘釘再「出招」

如果說之前釘釘的AI生態加持更多的圍繞資源和商業的底層助力&#xff0c;那么如今這種加持則是向更深層次進化&#xff0c;即真正的AI模型訓練能力加持&#xff0c;為垂類大模型創業者提供全方位的助力&#xff0c;提高創業成功率和模型產品商業化確定性。作者|皮爺出品|產業家…

XSS GAME靶場

要求用戶不參與&#xff0c;觸發alert(1337) 目錄 Ma Spaghet! Jefff Ugandan Knuckles Ricardo Milos Ah Thats Hawt Ligma Mafia Ok, Boomer Exmaple 1 - Create Example 2 - Overwrite Example 3 - Overwrite2 toString Ma Spaghet! <h2 id"spaghet&qu…

Unity學習筆記(五)——3DRPG游戲(2)

添加更多的敵人 編輯EnemyController&#xff0c;解決報錯導致敵人無法注冊觀察者模式&#xff0c;從而無法執行敵人慶祝動畫 using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.AI; public enum EnemyStatus { GUARD,PATROL…

2025測繪程序設計國賽實戰:一輪終章 | 單向后方交會C#實現

前言本文是小編對六道國賽試題中的最后一個試題&#xff0c;單向后方交會的一篇學習日志。本文的整體架構&#xff0c;依舊首先拿訓練數據跟大家介紹本題涉及到的數據的屬性含義&#xff0c;涉及到算法的原理、執行流程和終極目的。然后附上小編用C#來實現的程序&#xff0c;從…

基于Echarts的氣象數據可視化網站系統的設計與實現(Python版)

本系統旨在構建一個基于Echarts的氣象數據可視化系統&#xff0c;本系統能夠從中國天氣網爬取實時天氣數據&#xff0c;并進行存儲、分析和可視化展示。用戶可以通過網頁界面查看不同地區的天氣情況&#xff0c;以及歷史天氣數據的變化趨勢。 技術棧&#xff1a;Python語言、My…

HarmonyOS 啟動提速秘籍:懶加載全鏈路實戰解析

摘要 隨著移動應用功能越來越復雜、界面越來越豐富&#xff0c;應用啟動慢、內存占用高等問題也越來越普遍。特別是在 HarmonyOS NEXT 應用開發中&#xff0c;如果不加優化&#xff0c;用戶打開頁面時可能要等好幾秒&#xff0c;體驗就很差了。 懶加載&#xff08;Lazy Loading…

全新安裝Proxmox VE啟動時卡在Loading initial ramdisk

原因&#xff1a; 使用了Ventoy啟動盤裝載 Proxmox ISO 文件安裝。 要用Ventoy優盤啟動&#xff0c;選擇Advance Option里的Rescue Boot&#xff0c; 修改文件/etc/default/grub.d/installer.cfg&#xff0c;刪除rdinit/vtoy/vtoy運行 update-grub 更新grub配置&#xff0c;重啟…

【Java項目安全基石】登錄認證實戰:Session/Token/JWT用戶校驗機制深度解析

目錄 1.前言 2.正文 2.1Cookie—Session機制 2.1.1核心原理圖解&#xff1a; 2.1.2四步核心流程&#xff1a; 2.1.3存儲架構對比 2.1.4集群部署方案&#xff08;Spring Session Redis&#xff09; 2.2Token令牌 2.2.1核心原理圖解&#xff1a; 2.2.2四步核心流程&am…

融合優勢:SIP 廣播對講聯動華為會議 全場景溝通響應提速?

SIP 廣播對講與華為視頻會議融合解決方案&#xff0c;是基于 SIP 協議將廣播對講系統與華為視頻會議系統進行整合&#xff0c;實現通信資源共享與業務流程聯動&#xff0c;可提升應急響應效率與溝通協作能力。融合原理&#xff1a;SIP 是一種基于文本的應用層協議&#xff0c;具…

Milvus Dify 學習筆記

目錄 docker方式&#xff1a; 模式一&#xff1a;Milvus Lite linux docker方式&#xff1a; 下載yml文件&#xff0c; https://github.com/milvus-io/milvus/releases docker啟動&#xff1a; docker compose up -d from pymilvus import connections connections.conne…

汽車ECU控制器通信架構

我是穿拖鞋的漢子,魔都中堅持長期主義的汽車電子工程師。 老規矩,分享一段喜歡的文字,避免自己成為高知識低文化的工程師: 做到欲望極簡,了解自己的真實欲望,不受外在潮流的影響,不盲從,不跟風。把自己的精力全部用在自己。一是去掉多余,凡事找規律,基礎是誠信;二是…

【Linux】基本指令(入門篇)(上)

目錄 前言 1.目錄操作指令 1.1指令 1.2理論 1.2.1文件 1.2.2目錄與路徑 2.文件操作指令 2.1指令 2.2理論 2.2.1輸出與輸入 2.2.2一切皆文件 前言 這是Linux學習下的第一篇文章&#xff0c;后續Linux的學習也會持續更新分享。 Linux的基本指令是使用Linux操作系統的基礎…

正向代理與反向代理理解

問&#xff1a; 應用a請求ng&#xff0c;然后ng根據不同請求路徑將請求轉發到不同的服務器&#xff0c;對于應用a來說這個ng是正向代理角色還是反向代理呢&#xff1f; 答&#xff1a; 在這個場景中&#xff0c;Nginx 扮演的是反向代理的角色&#xff0c;而不是正向代理。以下是…

【Kafka】深入理解 Kafka MirrorMaker2 - 實戰篇

文章目錄一、把“家伙事兒”都備齊二、部署其實很簡單三、配置 MirrorMaker2四、修改啟動腳本五、集群啟動與驗證六、這集群“結實”嗎&#xff1f;聊聊它的高可用它沒有“大腦”&#xff0c;但活得很好極限測試&#xff1a;干掉兩個節點會怎樣&#xff1f;寫在最后最近在跟 Ka…

借助AI學習開源代碼git0.7之四update-cache

借助AI學習開源代碼git0.7之四update-cache update-cache.c 主要負責對索引&#xff08;index&#xff09;&#xff0c;也即緩存&#xff08;cache&#xff09;&#xff0c;進行增、刪、改操作。現在的高層命令 git add 的部分核心功能就是由這個代碼實現的。 核心功能 該程序的…

【48】MFC入門到精通——MFC 文件讀寫總結 CFile、CStdioFile、CFileDialog

文章目錄1 打開文件1.2 打開文件模式總結2 常用函數2.1 寫文件2.2 讀文件2.3 獲取文件長度3. 文件打開讀寫實力3.1 寫文件 覆蓋寫3.2 文尾追加寫3.3 換行寫4 文件對話框 CFileDialog4.2 文件對話框實例5 CStdioFile 類 讀寫CStingMFC提供了一個文件操作的基類CFile&#xff0c;…

Leetcode 124. 二叉樹中的最大路徑和

遞歸/*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}* TreeNode…