【OC底層】OC對象本質,如 isa, super-class

来源:https://www.cnblogs.com/xgao/archive/2018/09/28/9708163.html
-Advertisement-
Play Games

Objective-C的本質 Objective-C的本質 1、我們編寫的Objective-C,底層現實都是C/C++,代碼生成步驟如下: 1、我們編寫的Objective-C,底層現實都是C/C++,代碼生成步驟如下: 2、在OC中的所有面向對象的實現,都是基於C/C++的數據結構實現的 3、將 ...


Objective-C的本質

1、我們編寫的Objective-C,底層現實都是C/C++,代碼生成步驟如下:  

2、在OC中的所有面向對象的實現,都是基於C/C++的數據結構實現的

3、將Objective-C代碼轉換為C\C++代碼

  xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc OC源文件 -o 輸出的CPP文件

    註:如果需要鏈接其他框架,使用-framework參數。比如-framework UIKit

 

一個OC對象在記憶體中是如何佈局的? 

 

NSObject基類的實現:

 

子類的實現:

 

子類的拆解:

 1、在OC中的對象,就是 C++中的 struct來實現的

 2、每個OC對象中都會有一個 isa 的指針,isa指向的是 objc_class 結構體,如下:

通過代碼可以看得出在OC2 中已經不能通過  objc_method_list 之類的方式獲取方法名、實例、協議之類的了,需要使用新的方法獲取

思路:自定義一個和oc源碼中 objc_class 結構一樣的結構體,然後將對象的isa 強轉成我們自定義的那個,再去調用

如下是OC中objc_class的源碼:

新的OC版本中方法、屬性、協議相關數據都存在了 bits 

通過 bits.data() 返回 class_rw_t 結構,如下:

bits.data() 的實現:

其中需要 通過 & FAST_DATA_MASK 才能獲取到真實的地址

 

 OC對象的分類

Objective-C對象主要分為以下3類:

  1> instance對象(實例對象)

  2> class對象(對象)

  3> meta-class對象(元類對象)

 

通過下麵的代碼可以分別獲取3種對象:

        NSObject* obj = [[NSObject alloc]init];
        const char* className = [@"NSObject" cStringUsingEncoding:NSUTF8StringEncoding];
        
        // 實例對象
        NSLog(@"instance: %p",obj);
        
        // 類對象
        NSLog(@"NSObject class: %p", [NSObject class]);
        // 同上
        NSLog(@"obj class: %p", [obj class]);
        // 如果傳實例對象,獲取到的還是類對象
        NSLog(@"get class: %p", object_getClass(obj));
        // 通過類名獲取類對象
        NSLog(@"objcClass: %p", objc_getClass(className));
        
        // 元類對象
        // 必需要傳入類對象才能獲取元類對象
        NSLog(@"meta-class: %p", object_getClass([obj class]));
        // 通過類名獲取元類對象
        NSLog(@"objcMetaClass: %p", objc_getMetaClass(className));
        
        // 判斷是否是metaClass
        NSLog(@"isMetaClass1:%i",class_isMetaClass([NSObject class]));  // 0
        NSLog(@"isMetaClass2:%i",class_isMetaClass([[NSObject class] class])); // 0
        NSLog(@"isMetaClass3:%i",class_isMetaClass(object_getClass([NSObject class]))); // 1

 

輸出:

2018-09-27 14:39:37.363 OC_isa_supclass[1350:102454] instance: 0x100202b50
2018-09-27 14:39:37.364 OC_isa_supclass[1350:102454] NSObject class: 0x7fff760140f0
2018-09-27 14:39:37.364 OC_isa_supclass[1350:102454] obj class: 0x7fff760140f0
2018-09-27 14:39:37.364 OC_isa_supclass[1350:102454] get class: 0x7fff760140f0
2018-09-27 14:39:37.365 OC_isa_supclass[1350:102454] objcClass: 0x7fff760140f0
2018-09-27 14:39:37.365 OC_isa_supclass[1350:102454] meta-class: 0x7fff76014118
2018-09-27 14:39:37.365 OC_isa_supclass[1350:102454] objcMetaClass: 0x7fff76014118
2018-09-27 14:49:27.746 OC_isa_supclass[1360:106109] isMetaClass1:0
2018-09-27 14:49:27.746 OC_isa_supclass[1360:106109] isMetaClass2:0
2018-09-27 14:49:27.746 OC_isa_supclass[1360:106109] isMetaClass3:1

  1> 需要註意的是 objc_getClass 方法,當傳入的是實例對象,就會返回類對象,如果傳入的是類對象就會返回元類對象

  2> 還有一點就是 [[NSObject class] class] 這樣是獲取不到元類對象的,這樣獲取到的還是類對象

    3> 另外裡面還有兩個 objc_ 開頭的方法,分別是獲取類對象和元類對象,但它是傳入類名字元串就可以了,需要轉換成 C語言的char

1、instance對象

  instance對象就是通過類alloc出來的對象,每次調用alloc都會產生新的instance對象      object1、object2是NSObject的instance對象(實例對象)

  它們是不同的兩個對象,分別占據著兩塊不同的記憶體

 

  instance對象在記憶體中保存的信息包括:

  1> isa指針

  2> 其它成員變數

2、class對象

 

 objectClass1 ~ objectClass5都是NSObject的class對象(類對象)

 NSObject類對象只有一個,所有實例對象的class屬性獲取到的都是同一個類對象 

   class對象在記憶體中存儲的信息主要包括:

  1> isa指針

  2> superclass指針

  3> 類的屬性信息(@property)、類的對象方法信息(instance method)

  4> 類的協議信息(protocol)、類的成員變數信息(ivar)

    ....

3、meta-class對象

  

   objectMetaClass是NSObject的meta-class對象(元類對象)

   每個類在記憶體中有且只有一個meta-class對象

 

  meta-class對象在記憶體中存儲的信息主要包括:

   1> isa指針

   2> superclass指針

   3> 類的類方法信息(class method)

    ... 

 註:class 對象和 meta-class 對象都是 Class 類型的,它們其實結構都是一樣的,class對象中一樣會包含 類方法,只不過那個類方法是 空的而已。

   同樣,meta-class對象中也有 類的屬性、對象方法、協議、成員變數,不過那些對應的值也都是空

 

isa指針

上面我們通過源碼可以看到每個對象都有一個 isa 指針,isa指針作用是幹嘛的呢?

通過上圖可以看出:

  1> instanceisa指向class
    當調用對象方法時,通過instanceisa找到class,最後找到對象方法的實現進行調用

  2> classisa指向meta-class

    當調用類方法時,通過classisa找到meta-class,最後找到類方法的實現進行調用

 

class對象的superclass指針

superclass是用於找父類的,比如子類調用某個方法,如果子類中沒有,就會去父類找,底層就是通過superclass找到父類的,如下圖:

 當Studentinstance對象要調用Person的對象方法時,會先通過isa找到Studentclass,然後通過superclass找到Person的class,最後找到對象方法的實現進行調用

  

 meta-class對象的superclass指針

 meta-class中的superclass基本和 class對象中的一樣,不過有一點點區別,如圖:

 

  當Student的class要調用Person的類方法時,會先通過isa找到Student的meta-class,然後通過superclass找到Person的meta-class,最後找到類方法的實現進行調用

  有什麼區別呢?從這圖可能看不出來,區別就是如果基類meta-class中都找不到類方法,那麼它就會去從基類對象裡面去找對象方法,OC的底層其實是不區分 對象方法與類方法的。

 

isa、superclass總結 

 

 

這張圖能夠清楚的描述 isa和superclass的作用和關係,下麵是備註了一下,看得更加懂點。

 

  • instance的isa指向的是class對象
  • class的isa指向的是meta-class對象
  • meta-class的isa指向的是基類的meta-class對象
  • class的superclass指向的是父類的class對象,如果沒有父類,superclass指針為nil
  • meta-class的superclass指向的是父類的meta-class對象
  • 基類的meta-class指向的是基類的class對象(重點)

  instance調用對象方法的軌跡:

  實例對象會先通過isa找到class對象,判斷裡面有沒有要調用的方法,如果有就直接調用,沒有就會通過class對象中的superclass找到父類,然後在父類中判斷是否有該方法,如果還沒有就接著往上找。

   class調用類方法的軌跡:

   isa找meta-class,方法不存在,就通過superclass找父類,最後基類mate-class也沒有的話還會去基類對象找

 

 isa指針的一些問題

 上面已經說到了instance對象的isa指針指向的是class對象,那就是說instance對象的isa指針記憶體地址是不是就是class對象的記憶體地址呢?

 如果在以前的32位系統中確實如此,在64位系統中不是的,裡面有一個點操作, ISA_MASK

  其中arm64和x86架構的這個 ISA_MASK的偏移地址是不一樣的,如下圖:

 

 

 class、meta-class對象的本質結構都是struct objc_class,如下圖:

  

-----------------------------

本文參考借鑒MJ的教程視頻,非常感謝.


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1、windows圖標右鍵,選擇“電腦管理”; 2、展開左邊的“ 服務和應用程式” 選項,點擊“服務",找到 MySQL 伺服器,點擊左側的 "啟動",即可完成 MySQL伺服器的開啟。 PS: 1、未開啟MySQL伺服器之前,cmd命令行切換到MySQL目錄下的bin文件下,mysql -u r ...
  • 一、 選擇適合自己的Linux發行版 談到linux的發行版別,太多了,可能誰也不能給出一個準確的數字,但是有一點是能夠必定的,linux正在變得越來越盛行, 面臨這麼多的Linux 發行版,打算從別的體系轉到linux體系來的初學者可能會感到迷惑,即便是忠誠的 Linux 用戶也沒有時刻和精力去挨 ...
  • 絕大部分寫業務的程式員,在實際開發中使用 Redis 的時候,只會 Set Value 和 Get Value 兩個操作,對 Redis 整體缺乏一個認知。這裡對 Redis 常見問題做一個總結,解決大家的知識盲點。 1、為什麼使用 Redis 在項目中使用 Redis,主要考慮兩個角度:性能和併發 ...
  • MongoDB 是什麼 MongoDB 是一種非關係型資料庫(NoSQL)。 MongoDB中的術語解釋 文檔(document):形如 { name: "sue", 區分大小寫 field唯一 , 不可重覆 文檔可嵌套 鍵值對是有序的 集合:集合就是一組文檔 SQL 與 MongoDB 術語比較 ...
  • 自己的庫里有索引在用insert導入數據時會變慢很多 使用事務+批量導入 可以配置使用spring+mybatis整合的方式關閉自動提交事務(地址),選擇批量導入每一百條導入使用list存儲值傳入到mybatis中 http://x125858805.iteye.com/blog/2369243 或 ...
  • 最近公司項目需要從SQL Server轉到MySQL, 在轉的過程中遇到兩者語法之間的一些差異,在網上找瞭解決方案後,特記錄在此。由於解決方案可能有很多種,我只記錄了自己使用過的,僅作參考。 1. 拼接字元串 使用 方法 MSSQL MySQL 2. MySQL中和update set select ...
  • ...
  • AIDL使用以及IPC原理分析(進程間通信) 概要 為了大家能夠更好的理解android的進程間通信原理,以下將會從以下幾個方面講解跨進程通訊信: 1. 必要瞭解的概念 2. 為什麼要使用aidl進程間通信 3. 可能遇到的問題以及解決辦法 4. aidl的使用,通過android提供的aidl實現 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...