[iOS]類和對象的底層原探索

[iOS]類和對象的底層探索

文章目錄

  • [iOS]類和對象的底層探索
    • 繼承鏈(類,父類,元類)
      • instance 實例對象
      • class 類對象
      • meta-class 元類對象
    • 對對象、類、元類和分類的探索
      • instance 實例對象
      • class 類對象
      • meta-class 元類對象
      • 分類(category)
    • isMemberOfClass & isKindOfClass(類和對象)

有些部分前面講的不是很清楚
或者沒講的很仔細的
在這一篇重新拿出來記錄一下
也當作是復習一遍

繼承鏈(類,父類,元類)

先來結論

instance 實例對象

instance對象就是通過alloc方法創建出來的對象,每次調用alloc方法都會生成新的instance對象

instance對象在內存中存放的信息包括

  1. isa指針
  2. 其他成員變量

class 類對象

class對象的作用是用來描述一個instance對象,它內部存放一個類的屬性信息(@property)、對象方法信息(instance method)、協議信息(protocol)、成員變量信息(ivar),另外class對象里面還有兩個指針,isa指針 和 superclass指針。

Objective-C類是由Class類型來表示的,它實際上是一個指向objc_class結構體的指針

類對象就是一個結構體struct objc_class,這個結構體存放的數據稱為元數據(metadata),
該結構體的第一個成員變量也是isa指針,這就說明了Class本身其實也是一個對象,因此我們稱之為類對象
類對象在編譯期產生用于創建實例對象,是單例

meta-class 元類對象

meta-class對象的作用是用來描述一個class對象

跟class一樣,元類對象在內存中也是只有一份的
它內部存儲了 isa指針 + superclass指針 + 類方法信息(+方法)

類對象中的元數據存儲的都是如何創建一個實例的相關信息
那么類對象和類方法應該從哪里創建呢? 就是從isa指針指向的結構體創建
類對象的isa指針指向的我們稱之為元類(metaclass), 元類中保存了創建類對象以及類方法所需的所有信息

來看這張很經典的圖
在這里插入圖片描述
通過上圖我們可以看出整個體系構成了一個自閉環,struct objc_object結構體實例它的isa指針指向類對象
類對象的isa指針指向了元類,super_class指針指向了父類的類對象
而元類的super_class指針指向了父類的元類,那元類的isa指針又指向了自己

為什么要有元類?

元類(Meta Class)是一個類對象的類。在上面我們提到,所有的類自身也是一個對象,我們可以向這個對象發送消息(即調用類方法)。為了調用類方法,這個類的isa指針必須指向一個包含這些類方法的一個objc_class結構體。這就引出了meta-class的概念,元類中保存了創建類對象以及類方法所需的所有信息。任何NSObject繼承體系下的meta-class都使用NSObject的meta-class作為自己的所屬類,而基類的meta-class的isa指針是指向它自己

OC的類其實也是一個對象
一個對象就要有一個它屬于的類
意味著類也要有一個isa指針指向其所屬的類
那么類的類是什么
就是我們所說的元類(MetaClass)
所以,元類就是類的所屬類

既然元類是個類那元類的類是什么呢?
所有的元類都使用根元類作為他們的類
根元類的 isa 指針指向了它自己

對對象、類、元類和分類的探索

法一

可以打開前面提到過的runtime源碼查看相關定義

法二

將OC代碼轉換為C/C++代碼之后再對其進行研究

法二可行的理由是因為OC本質底層實現轉化其實都是C/C++代碼

咱們今天先通過法二進行探索 同時學習一下轉化OC代碼的操作

gcc -rewrite-objc main.m

使用上面指令可以轉化OC代碼
但是如果你的代碼內引用了UIKit之類的庫
那么會出現這么一條報錯
請添加圖片描述
好 怎么辦呢

可以通過加一些參數指定使用 iOS 系統的 SDK

gcc -rewrite-objc -fobjc-arc -fobjc-runtime=ios-13.0.0 -isysroot/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.0.sdk main.m

也可以通過xcrun工具
Xcode安裝的時候順帶安裝了xcrun命令,xcrun命令在clang的基礎上進?了?些封裝,要更好??些

xcrun -sdk iphoneos gcc -rewrite-objc main.m

instance 實例對象

對于我們的MYObject對象

請添加圖片描述
找到它的對應部分
在這里插入圖片描述

typedef struct objc_object MYObject;
這里使用 typedef 為 struct objc_object 結構體定義了一個別名 MYObject
所以對象本質上就是結構體

typedef struct {} _objc_exc_MYObject;
同樣是使用 typedef 定義了一個名為 _objc_exc_MYObject 的結構體類型,不過這里的結構體沒有具體的成員。

struct MYObject_IMPL {...};
這是定義了一個名為 MYObject_IMPL 的結構體。該結構體包含了另一個結構體 NSObject_IMPL NSObject_IVARS,以及兩個 NSString * 類型的成員變量 _age 和 _Nonnull _name

“IMPL”常見的是“implementation”的縮寫,意思是“實現”。在編程中,它通常用于表示某個類、方法或功能的具體實現部分。例如,可能會有一個接口(interface)定義了一些方法,而“IMPL”后綴的類則是具體實現這些方法的類。

class 類對象

我們接著找到NSObject_IMPL的定義部分
在這里插入圖片描述

關于NSObject的具體實現部分只有一個Class類型的isa指針
那么再看看Class類型到底是何方神圣

在這里插入圖片描述

可以看出Class底層是objc_class *類型
咱們前面講過objc_class結構體本質上代表了類對象

由此回收第一個結論

類對象中的元數據存儲的都是如何創建一個實例的相關信息

同時發現

  • id底層實現是struct objc_object *類型,定義為了一個Class的指針。
  • SEL是struct objc_selector *類型
  • IMP是void (*)(void)函數指針類型

接下來我們要找objc_class *的定義
但是在這個轉化的文件內 找不到objc_class *的定義了
好 開始挑源碼來看

定義了洋洋灑灑五百行

方法存儲在公共內存位置,提供給所有的實例對象使用、類對象使用

所以咱除去方法,主要看看其成員變量

struct objc_class : objc_object {// Class ISA;Class superclass;cache_t cache;             // formerly cache pointer and vtableclass_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
}

好 可能和大家在其他博客內看到的不太一樣
上面是在2006年蘋果發布Objc 2.0之后,objc_class的定義
之前的長這樣

struct objc_class {Class isa  OBJC_ISA_AVAILABILITY;#if !__OBJC2__Class super_class                                        OBJC2_UNAVAILABLE;const char *name                                         OBJC2_UNAVAILABLE;long version                                             OBJC2_UNAVAILABLE;long info                                                OBJC2_UNAVAILABLE;long instance_size                                       OBJC2_UNAVAILABLE;struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;struct objc_cache *cache                                 OBJC2_UNAVAILABLE;struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;
#endif} OBJC2_UNAVAILABLE;

分析一下

superclass 指針指向該類的父類,通過它可以建立類的繼承關系。
cache(以前是緩存指針和虛函數表)用于存儲方法等的緩存信息,以提高方法查找速度。
bits 通過位運算等方式存儲和管理了一些相關信息及自定義標志,例如類的讀寫權限、是否有自定義的內存分配標志等。

雖然這些成員變量本身并不直接包含所有的類信息,但通過它們以及與之相關的各種方法和運行時機制,可以訪問到類的其他詳細信息,如屬性、方法列表、協議列表等。這些信息可能通過間接的方式存儲、計算或在運行時動態獲取。

將源碼的定義轉化為類圖

在這里插入圖片描述

NSObject本質上是一個叫做NSObject_IMPL的結構體
其成員變量isa本質上也是一個指向objc_class結構體的指針(objc_class繼承自objc_object結構體,內部有一個isa成員)

meta-class 元類對象

其實元類和類的本質都是objc_class結構體,只不過它們的用途不一樣,類的methods成員變量里存儲著該類所有的實例方法信息,而元類的methods成員變量里存儲著該類所有的類方法信息

分類(category)

分類是OC的一個高級特性,我們一般用它給系統的類或者第三方庫的類擴展方法,屬性和協議,或者把一個類不同功能分散到不同的模塊中實現
來看看它的源碼


同樣屬于結構體

說法一
可以看到里面是沒有成員變量列表存在的,這樣可以解釋分類為什么不能添加成員變量
說法二
因為category是運行時添加的,他只能操作class_rw_t結構,但是class_rw_t結構沒有添加成員變量的入口。成員變量是存儲在class_ro_t中的,是無法被修改的,所以category就不能添加成員變量。

在 Objective-C 中,分類(category)不能添加成員變量。這是因為分類在運行時動態添加,它只能操作 class_rw_t 結構,而 class_rw_t 結構沒有提供添加成員變量的入口。成員變量的信息是存儲在 class_ro_t 中的,并且在運行時是不可修改的。第一種說法不準確,不能僅僅因為源碼中分類的定義沒有成員變量列表就得出分類不能添加成員變量的結論,關鍵在于運行時的結構和機制限制

哦對了要注意有一個點

分類中的方法與原始類以及父類方法相比具有更高優先級,如果覆蓋父類的方法,可能導致super消息的斷裂。因此,最好不要覆蓋原始類中的方法

我們知道一個類的實例方法存儲在類里面,所有的類方法在元類里面,而對象調用方法isa指針先到相應的類的和元類,然后在其方法列表中查找
那么分類是這么實現這一功能的?

留個懸念 這周進度不夠了 我得先寫其他博客

isMemberOfClass & isKindOfClass(類和對象)

老規矩 上總結

圖解isKindOfClass和isMemberOfClass方法

總結

當調用者是——類對象SubClass

+(BOOL)isKindOfClass:(Class)cls :

判斷cls是不是 元類->父類的元類->父父類的元類->…->根元類->NSObject (元類的superclass繼承鏈)其中一個。
cls 傳除NSObject.class外的任意類對象均為false。

+(BOOL)isMemeberOfClass:(Class)cls :

判斷cls是不是自己的isa,

當調用者是——元類MetaClass

+(BOOL)isKindOfClass:(Class)cls :

判斷cls是不是 根元類->NSObject 中的任意一個

+(BOOL)isMemeberOfClass:(Class)cls :

判斷cls是不是根元類

當調用者是——對象Obj

-(BOOL)isKindOfClass:(Class)cls :

判斷cls是不是類對象->父類->…->NSObject(superclass繼承鏈)其中一個

-(BOOL)isMemeberOfClass:(Class)cls :

判斷 cls 是不是自己的 類(類對象)

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

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

相關文章

react項目使用EventBus實現登錄攔截

關于EventBus EventBus是一個事件發布/訂閱模式的實現,它允許不同的組件或模塊之間進行通信,而不需要直接引用對方。這種模式特別適用于那些需要跨組件傳遞信息,但又不想引入復雜依賴關系的場景。 實現思路 利用axios中間件,在…

防火墻之帶寬管理篇

核心思想 1.帶寬限制:限制非關鍵業務流量占用帶寬的比例 2.帶寬保證:保證關鍵的業務流量傳輸不受影響。業務繁忙時,確保業務不受影響。 3.限制連接數:可以針對某些業務進行連接數的限制,首先可以降低該業務占用帶寬…

基于UltraFace的人臉檢測在地平線旭日X3派上的部署和測試(Python版本和C++版本)

電腦端的測試環境搭建 如果不想再搭建環境和測試代碼bug上浪費更多的時間可以直接獲取本人的測試虛擬機,所有的測試代碼、虛擬環境和板端測試工程以全部打包到了虛擬機,需要的可以通過網盤獲取: 代碼和虛擬機百度網盤鏈接: 鏈接…

【AI繪畫教程】Stable Diffusion 1.5 vs 2

在本文中,我們將總結穩定擴散 1 與穩定擴散 2 辯論中的所有要點。我們將在第一部分中查看這些差異存在的實際原因,但如果您想直接了解實際差異,您可以跳下否定提示部分。讓我們開始吧! Stable Diffusion 2.1 發布與1.5相比,2.1旨在解決2.0的許多相對缺點。本文的內容與理解…

網絡和安全操作

一、編輯文件 文本編輯器有很多,比如圖形模式的gedit、OpenOffice 等,文本模式下的編輯器有vi、vim(vi的增強版本)等。vi和vim是我們在Linux中最常用的編輯器。 gedit:類似于windows下的記事本,很方便的去…

IO多路復用技術、select、poll、epoll聯系與區別

目錄 IO多路復用技術select:poll:epoll(Linux特有): epoll select poll的區別epoll是同步還是異步epoll詳解 IO多路復用技術 通信雙方都有一個socket,以一個文件描述符的形式存在,那這個fd也對…

AI 大事件:超級明星 Andrej Karpathy 創立AI教育公司 Eureka Labs

🧠 AI 大事件:超級明星 Andrej Karpathy 創立AI教育公司 Eureka Labs 摘要 Andrej Karpathy 作為前 OpenAI 聯合創始人、Tesla AI 團隊負責人,他的專業性和實力備受矚目。Karpathy 對 AI 的普及和教育充滿熱情,從 YouTube 教程到…

CBSD bhyve Ubuntu 配置vnc登錄管理

CBSD介紹 CBSD是為FreeBSD jail子系統、bhyve、QEMU/NVMM和Xen編寫的管理層。該項目定位為一個綜合解決方案的單一集成工具,用于使用預定義的軟件集以最少的配置快速構建和部署計算機虛擬環境。 雖然CBSD沒有提供額外的操作系統級功能,但它極大地簡化了…

兩年經驗前端帶你重學前端框架必會的ajax+node.js+webpack+git等技術 Day1

黑馬程序員前端AJAX入門到實戰全套教程,包含學前端框架必會的(ajaxnode.jswebpackgit),一套全覆蓋 Day1 你好,我是Qiuner. 為幫助別人少走彎路和記錄自己編程學習過程而寫博客 這是我的 github https://github.com/Qiuner ?? ?…

【算法/天梯賽訓練】天梯賽模擬題集

L1-009 N個數求和 #include <iostream> #include <algorithm>using namespace std;typedef long long ll; const int N 105;typedef struct node {ll x, y; }node; node a[N];ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a; }int main() {int n;cin >>…

《昇思25天學習打卡營第25天|第9天》

今天是打卡的第九天&#xff0c;今天學習的是使用靜態圖加速這門課程&#xff0c;從他的背景學起&#xff1a;AI編譯框架分為兩種運行模式&#xff0c;分別是動態圖模式和靜態圖模式&#xff0c;動態圖模式特點&#xff1a;計算圖的構建和計算同時發生&#xff0c;缺點&#xf…

Qt Style Sheets-樣式表語法

樣式表語法 Qt 樣式表術語和語法規則幾乎與 HTML CSS 的相同。如果您已經了解 CSS&#xff0c;您可能可以快速瀏覽此部分。 樣式規則 樣式表由一系列樣式規則組成。樣式規則由選擇器和聲明組成。選擇器指定哪些小部件受該規則影響&#xff1b;聲明指定應在小部件上設置哪些屬性…

ThinkPHP6事件系統使用指南

本文由 ChatMoney團隊出品 在ThinkPHP 6中&#xff0c;事件系統提供了一種優雅的方式來實現解耦和動態響應。你可以通過注冊事件和對應的監聽者來處理各種應用邏輯。 事件注冊 閉包注冊 閉包是最簡單的事件監聽者&#xff0c;可以直接在注冊時定義。 Event::listen("C…

Linux操作系統之多文件管理

makefile: makefile文件用于管理和組織代碼工程的編譯和鏈接,被make工具解析并完成相關動作 make: 工程管理工具 語法: 要生成的文件:依賴的所有文件 時間戳: 編譯文件時,時間戳更新的文件需要重新加入編譯,時間戳沒有改變的不需要重新編譯 app:main.c add.c sub.c …

如何追查一個packet在linux 系統哪里丟失

要想追一個包在系統哪里丟失了&#xff0c; 就要了解 一個應用層的包在送出時 要經歷那些 檢查點 和被丟掉的點。 1. 在傳輸層&#xff0c;如果是 tcp 包 會有contrack 的 buf 的限制 可能會導致 packets 的丟失。 > 檢查辦法&#xff1a;查看dmesg日志有報錯&#xff1a;k…

MySQL數據庫慢查詢日志、SQL分析、數據庫診斷

1 數據庫調優維度 業務需求&#xff1a;勇敢地對不合理的需求說不系統架構&#xff1a;做架構設計的時候&#xff0c;應充分考慮業務的實際情況&#xff0c;考慮好數據庫的各種選擇(讀寫分離?高可用?實例個數?分庫分表?用什么數據庫?)SQL及索引&#xff1a;根據需求編寫良…

C# 實體更新記錄:如何捕獲和記錄字段變化到日志

方案一&#xff1a;粗糙但可用 var changes new List<string>();void CompareAndAddChange<T>(string propertyName, T oldValue, T newValue, Func<T, string> descriptionFunc null) {if (!EqualityComparer<T>.Default.Equals(oldValue, newVa…

分支定界法(Branch and Bound, 簡稱BB)是一種求解整數規劃問題的有效算法。

分支定界法&#xff08;Branch and Bound&#xff09;詳解與Python代碼示例 分支定界法概述 分支定界法&#xff08;Branch and Bound, 簡稱B&B&#xff09;是一種求解整數規劃問題的有效算法。它結合了搜索與迭代的思想&#xff0c;通過系統地枚舉候選解來尋找最優解。在…

Java Web常見框架尋找路由技巧

在Java Web代碼審計中&#xff0c;尋找和識別路由是很關鍵的部分。通過注冊的路由可以找到當前應用對應的Controller&#xff0c;其作為MVC架構中的一個組件&#xff0c;可以說是每個用戶交互的入口點。簡單介紹下Java Web中常見框架&#xff08;Spring Web、Jersey&#xff09…

【SASS/SCSS(二)】模塊化語法

目錄 一、use 1、命名空間 2、私有變量 3、用with改變模塊中的默認值 二、forward 1、給forward模塊起別名&#xff0c;讓成員加前綴 2、利用hide or show手動控制成員的可訪問性 三、import 1、不存在命名空間&#xff0c;成員變量在import之后直接公開 2、可以在嵌…