序言
- Objective-C是一門動態性比較強的編程語言,跟C、C++等語言有著很大的不同
- Objective-C的動態性是由Runtime API來支撐的
- Runtime API提供的接口基本都是C語言的,源碼由C\C++\匯編語言編寫
位運算和共用體
位運算
程序中的所有數在計算機內存中都是以二進制的形式儲存的。位運算說穿了,就是直接對整數在內存中的二進制位進行操作。
C++提供了6種位運算符來進行位運算操作:
- & 按位與
- | 按位或
- ^ 按位異或
- ~ 按位取反
- << 左移(左邊消失,右邊補0)
- >> 右移(右邊消失,左邊補符號位)
位運算的操作數是整數類型或字符型.
1.按位與&運算
相同為一
& 運算常常用來將某變量的某些位清0
& 也常用于二進制取位操作
2.按位或|運算
有一則一
|運算通常用于二進制特定位上的強制置1
3.按位異或^運算
^運算通常用于對二進制的特定一位進行取反操作
共用體
共用體把幾種不同數據類型的變量存放在同一塊內存里。共用體中的變量共享同一塊內存。
定義共用體類型變量的一般形式:
union 共用體名
{
成員列表
}變量列表;
union的主要特征有
- union中可以定義多個成員,union的大小由最大的成員的大小決定;
- union成員共享同一塊大小的內存,一次只能使用其中的一個成員;
- 對union某一個成員賦值,會覆蓋其他成員的值(但前提是成員所占字節數相同,當成員所占字節數不同時只會覆蓋相應字節上的值,比如對char成員賦值就不會把整個int成員覆蓋掉,因為char只占一個字節,而int占四個字節);
- union量的存放順序是所有成員都從低地址開始存放的。
一 isa詳解
- 要想學習Runtime,首先要了解它底層的一些常用數據結構,比如isa指針
- 在arm64架構之前,isa就是一個普通的
指針
,存儲著Class、Meta-Class對象的內存地址
- 從arm64架構開始,對isa進行了優化,變成了一個
共用體(union)
結構,還使用位域
來存儲更多的信息
- isa結構體
/** isa_t 結構體 */
union isa_t {
Class cls;
uintptr_t bits;
struct {
uintptr_t nonpointer : 1;
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 33;
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 19;
};
};
實例代碼如下 - 一定需要用真機進行調試
#import <Foundation/Foundation.h>
@interface CSPerson : NSObject
@end
創建一個對象并且斷點調試,輸出 isa 值
接下來我們創建一些關聯對象和弱引用對象
isa參數詳解
nonpointer
:
0,代表普通的指針,存儲著Class、Meta-Class對象的內存地址
1,代表優化過,使用位域存儲更多的信息has_assoc
:是否有設置過關聯對象,如果沒有,釋放時會更快has_cxx_dtor
:是否有C++的析構函數(.cxx_destruct),如果沒有,釋放時會更快shiftcls
:存儲著Class、Meta-Class對象的內存地址信息magic
:用于在調試時分辨對象是否未完成初始化weakly_referenced
:是否有被弱引用指向過,如果沒有,釋放時會更快deallocating
:對象是否正在釋放extra_rc
:表示該對象的引用計數值,實際上是引用計數值減 1,例如,如果對象的引用計數為 10,那么 extra_rc 為 9。如果引用計數大于 10,則需要使用到下面的 has_sidetable_rc。has_sidetable_rc
:當對象引用計數大于 10 時,則has_sidetable_rc 的值為 1,那么引用計數會存儲在一個叫 SideTable 的類的屬性中,這是一個散列表。
為什么要&ISA_MASK來獲取類或元類的地址
因為從arm64位開始,isa里面存儲各種信息,是一個共用體,其中shiftcls
33位才是用來存放地址。通過&ISA_MASK就可以將33位的地址值取出來。
無論是實例對象,還是類對象,還是元類對象,他們的地址最后一位要么是0,要么是8,因為他們的地址是
isa
&ISA_MASK
,又因為ISA_MASK
最后3位都是0,所以導致他們的地址最后3位也永遠是0,所以最后一位要么是0(0000 0000)
,要么是8(0000 1000)
。
本文主要參考MJ老師的教案,非常感謝MJ老師。
關于runtime更多文章請看如下鏈接
iOS-runtime-API詳解+使用
iOS Runtime原理及使用
iOS - runtime如何通過selector找到對應的 IMP地址(分別考慮類方法和實例方法)
iOS - Runtime之面試題詳解一
iOS-runtime之面試題詳解二
iOS runtime的使用場景-實戰篇