OC底層-元類的底層探索

src=http___img9.doubanio.com_view_note_l_public_p60856515.jpg&refer=http___img9.doubanio.jpeg

前言

在以前的學習中,咱們已經知道,類的isa指針經過&mask掩碼,找到了一個與類如出一轍的類,可是兩個類的地址不一樣,後來通過探索得知,該類實際上是系統自動生成的,也就是元類,那麼元類存在的意義究竟是什麼?本篇主要討論的重心就是於此。html

WWDC關於類的dirty Meomory和clean Memory

6F808D60B8798122EA4352A0FED7606C.png

如圖:類在二進制文件表現包括了元類,父類,flags,methodCache,固然還有類自己,類對象自己含有指向元類,父類,方法緩存的指針,同時,他還有可以指向一片乾淨內存(clean Memory)的指針,也就是ro(只讀),此部份信息不可修改。swift

588D69AE08D072AE400028028BB21173.png

相對的,就存在dirty Meomory。rw就是在運行時動態存在對類的內容進行修改,此部分伴隨着程序運行一直存在,而且在一個類開始被使用時,系統會自動分配這塊內存容量用於讀寫。api

First Subclass 、 Next Sibling Class:全部的類都會連成一個樹狀結構,使得你在運行時能夠便利全部使用的類。緩存

Methods、Properties、Protocols:容許你在運行時動態的改變方法、屬性(分類)。markdown

Demangled Name:因爲swift和oc是共享這片結構體,但只有swift會使用這個字段,而且只有在詢問oc名稱時纔會使用到。app

1.png

因爲在平時使用中,不多會有存在大量使用Methods、Properties、Protocols、Demangled Name,因此蘋果對這部分作了優化,但願可以空出更多的髒地址使用的空間,因此單獨開闢了了一塊結構體用於擴展(ext_t)。ide

問題遺留

問題1:

存在兩個類,LGTearcher繼承了LGPerson,可是在打印LGPerson信息時並無在firstSubclass中發現LGTeacher。函數

2.png

解決:

在調用了LGTeacher.class之後,能夠打印出。oop

3.png

緣由:

類的調用是懶加載。學習

問題2:

類裏成員變量沒有打印出來

分析:

成員變量屬於只讀,不在properties方法中

解決:

經過搜索ro方法查到了其中的實現。

4.png

因而調用:ro()方法

5.png

補充對協議的打印

截屏2021-06-27 下午1.09.30.png

截屏2021-06-27 下午1.09.53.png

截屏2021-06-27 下午1.10.00.png 總結一下對類裏面數據的獲取: 獲取屬性,是根據.properties()的方法 獲取成員變量,是根據ro() 獲取對象方法,是根據.methods(),取的時候要取對應的big函數,由於他沒有實現打印description方法

成員變量與屬性的本質區別

經過clang對文件編譯得到cpp文件,能夠看到底層對屬性已經生成了set和get方法。

8A0E1AE4-89D0-495C-BA2B-FBDAB5CB570D.png

擴展:

關於特殊符號,能夠參考ivar_getTypeEncoding獲取typeEncoding,先附上官方文檔地址: typeEncoding

V:返回空(能夠看到該方法是set方法)

@:id (能夠看到當前是get方法,由返回值) 16/24:表示一共佔用的字節

@ : 參數 id self

0:從0號位置開始

: : SEL

8 : 從8號位置開始

16 = 8 + 8

24 = 16 + 8

類的結構

問題

在編譯後的cpp文件中,咱們發現屬性有的是objc_setProperty方法,有的是內存平移,也就是說實現的方式不一樣。

思考:

在給類的屬性賦值時,oc會根據你的屬性找到對應的set 和 get 方法,可是如何經過定義好的屬性去生成對應的set 和 get方法呢?在上層來看,set就是賦值,get就是拿值,而在於底層來看,不論是set仍是get,都是基於cmd的方法,只是名字不一樣而已,因此能夠進行統一的封裝處理。可是從上層到底層沒法直接通訊,我的理解是須要一箇中間層去把上層的數據處理好,給底層使用,相似於用一個通用的方法去統一處理。 過程猜測:類在加載的時候能夠生成ivars,根據ivars的sel去找到對應的imp,可是此時imp尚未實現,那麼目前oc中是用了objc_setProperty重定向的方式。結合最開學的分類的一些知識能夠知道,咱們如今的賦值操做並不會改變這個類,因此objc_setProperty天然也不會在運行時產生,另外,若是在運行時再去操做對系統壓力太大(objc源碼),並且若是尚未實現,那麼在運行時去查找很容易出錯,不太容易能查到對方的方法。由此能夠經過對llvm的查找得出他在底層的實現原理。

探索:

第一步:打開了LLVM之後,根據objc_setProperty 找到了生成他的方法

98B74CB6-A369-47D0-B5AE-425E4FCE3347.png

第二步:從下而上找,哪裏調用了這個方法來建立這個runtime的objc_setProperty方法呢?全局搜索getSetPropertyFn,找打了GetPropertySetFunction方法調用,只是作中間層跳轉,不用管,搜索GetPropertySetFunction方法。如今咱們知道是別的地方調用了這個方法從而纔會有全局搜索getSetPropertyFn,可是在哪一種狀況下調用的,上層究竟是通過了什麼判斷不得而知,那麼如今須要知道的就是上層的處理邏輯。

CCAEAFDB-C3D9-47BC-BD21-61FDC776135A.png

第三步:沿着GetPropertySetFunction方法,能夠找到調用判斷的依據,有native,有GetSetProperty等等,猜測多是不一樣的判斷會走不一樣的屬性或者是成員變量的賦值流程。如今只要找到給判斷賦值的依據便可。

9D0570FA-3D40-4AD1-8E6B-92231D67A597.png

E24BE680-E0AC-4016-98BC-A6F7ED17B137.png

第四步:根據PropertyImplStrategy找到他的賦值以及定義的地方,能夠看到copy的時候會判斷加入方法,並且他都是默認,就能夠猜測,copy關鍵字會影響到最終會不會調用GetSetProperty方法。

FB66F2EC-C712-4BFB-BA09-C34CE369716B.png

FD508F66-A92D-4383-9BE9-075425AB97DB.png

類的方法

問題1:

在以前的探索學習中,仍然缺乏對類方法的打印輸出。

思考:

方法不一樣於成員變量,方法存儲在類中,防止實例化對象產生重複的方法浪費內存,同理可得,爲了避免浪費內存,全部的類方法存在在元類中。

方法一

通過lldb打印驗證,這裏就不貼圖了。

方法二

經過api打印

方法三

爛蘋果查看

問題2:

類的對象方法能夠在類中找到,類的類方法是元類的對象方法,類的類方法與對象方法同名依然不會有問題,因此在打印的類和元類的方法列表的時候,類的對象方法元類是沒有的,類的類方法在類裏是打印不出來的,那爲何在元類中的「類方法」仍然能夠被元類找到?

解決

經過對底層代碼的簡單解析,發現獲取類方法的過程就是獲取元類的對象方法,點進去,系統已經有了判斷,若是是元類的話直接返回,也就是說元類裏面能夠直接查看自己的類方法了。說到這裏,基本能夠判斷,在底層是不存在類或者對象的概念,都是對象方法,只不過取決於你從哪裏取,若是是類,那就只能從 元類取,元類的話就從自己取。 6EC9C91E-5A13-482B-A34A-364D5915C5B1.png

EAB3D68F-327E-4609-9FDB-CA79676F7EE2.png

補充注意

1.在獲取bits數據強轉時,必定要注意平移32位,否則強轉的數據存在問題打印出來count有問題。 2.在打印元類的地址時,每每發現和類的isa的地址是同樣的,可是對象的isa和類的isa不同。isa的內部存儲的是類信息+其餘信息、引用計數、weak表等,然後者這些都是針對於對象,與類來講是不存在的,因此類的isa地址和元類的地址是同樣的。

後記

第一次寫,但願提出寶貴意見,互相監督,一塊兒學習。感謝!另外,我有個大膽的想法,那就是鎖住co某人的喉~~

相關文章
相關標籤/搜索