isa
在哪裏有以下代碼,在控制檯輸出obj
的數據結構,排在第一位的就是isa
的地址。 數據結構
爲何呢?由於對象繼承自NSObject
,NSObject
在底層的實現是結構體objc_object
,裏面只有一個isa
成員變量,那麼對象的首地址指向的第一塊就是isa
所在。架構
isa
的類型正常來講isa
指向的就是該對象的類,那咱們打印這個地址應該輸出類名,可是出乎意料的是這裏並無打印出類名。 ide
去源碼裏面找找看,發現isa
是Class
類型函數
Class
類型實際上是objc_class
post
能夠看到objc_class
繼承自objc_object
,那麼裏面就應該有一個isa
。此外還有的成員變量就是superclass
、cache
、bits
、data
。 優化
isa
的結構可是不行,這樣也看不出來什麼呀,仍是不知道isa
究竟是什麼。有看過 iOS alloc & init 方法解析 的朋友應該有印象,在alloc
方法裏面會調用一個叫initIsa()
的方法,那麼是否是能夠在這個方法中找到isa
的真正結構呢?3d
終於找到了,原來isa_t
是個聯合體,看下來重點就應該在ISA_BITFIELD
指針
喜大普奔終於找到了,注意的咱們要關注arm64
下的結構。前面的是參數名、後面的是所佔位數,總數加起來是64位。 調試
參數名 | 做用 | 大小 | 所在位置 |
---|---|---|---|
nonpointer | 是否對isa 指針開啓指針優化0:純 isa 指針只包含類對象地址1: isa 中包含了類對象地址、類信息、對象的引用計數等 |
1 | 0 |
has_assoc | 是否有關聯對象 0:沒有 1:存在 |
1 | 1 |
has_cxx_dtor | 該對象是否有C++ 或者Objc 的析構器 若是有析構函數則須要作析構邏輯 若是沒有則能夠更快的釋放對象 |
1 | 2 |
shiftcls | 存儲類指針的值。開啓指針優化的狀況下,在arm64 架構中有 33 位用來存儲類指針 |
33 | 3~35 |
magic | 用於調試器判斷當前對象是真的對象仍是沒有初始化的空間 | 5 | 36~40 |
weakly_referenced | 是否有弱引用 0:沒有 1:存在 |
1 | 41 |
deallocating | 是否正在釋放內存 0:不是 1:是 |
1 | 42 |
has_sidetable_rc | 是否須要用到外掛引用計數,當對象引用技術大於 10 則須要借用該變量存儲進位 | 1 | 43 |
extra_rc | 該對象的引用計數值,其實是引用計數值減 1。 若是對象的引用計數爲10,那麼 extra_rc 爲 9。若是引用計數大於 10 則須要使用 has_sidetable_rc |
19 | 44~63 |
那麼咱們知道了,在開啓isa優化
的時候對象的指針是存在isa
的shiftcls
裏面,那麼怎麼得到shiftcls
呢?咱們強轉isa
的類型爲isa_t
後打印code
isa
獲取類shiftcls
是有了,可是這裏面還不是類?這時候咱們再來看shiftcls
是怎麼來的。
原來是cls
右移3位。那麼咱們把它還原一下,成功~
日常獲取對象的類會直接調用class
方法,那麼class
方法內部實現是怎樣的?
重點來了,getIsa()
方法;
當前不是taggedPointer,直接返回ISA()
ISA()
裏面根據判斷條件就會走到最後的一行,就是isa.bits
和ISA_MASK
作一下與運算
咱們來看看ISA_MASK
ISA_MASK
有了,可是isa.bits
怎麼獲取呢??這裏打印isa
出來的數值和結構裏bits
的值如出一轍!
那麼咱們嘗試一下~把isa
打印的值和ISA_MASK
與運算~成功獲取到類
ISA
的指向這個時候已經驗證了能夠經過對象的isa
獲取到對象的類,那麼類自己也是一個對象,它的isa
又是指向那裏呢?
LGPerson
,第一層是咱們的對象,第二層是類對象。第三層雖然也是LGPerson
,可是地址和第二層的不同,說明不是同一個對象。類對象在內存中是隻能存在一個的,那麼第三層確定就是元類了。isa
指向的是NSObject
,NSObject
的象isa
指向的也是NSObject
。看看2個NSObject
的地址是相同的,因此NSObject
的isa
是指向了自身,也就是NSObject
和LGPerson
具備同一個根元類。
那麼咱們能夠獲得四個結論
isa
指向其類isa
指向其元類isa
指向根元類isa
都指向根元類