Runtime 01 - isa
Runtime 又叫運(yùn)行時,是一套 C 語言的 API辣往,Objective-C 是一門動態(tài)編程語言愤兵,其動態(tài)性是由 Runtime 來支撐和實現(xiàn)的,Runtime 源碼由 C/C++/匯編語言編寫排吴。
在使用 Objective-C 編碼的過程中,可以給任意一個對象發(fā)送消息懦鼠,在編譯階段只是確定了要向接受者發(fā)送這條消息钻哩,而接收者如果響應(yīng)和處理消息屹堰,在運(yùn)行時階段來決定。
平時編寫的 Objective-C 代碼街氢,都會由編譯器轉(zhuǎn)換成運(yùn)行時代碼扯键,很多操作會推遲到運(yùn)行時再進(jìn)行。
當(dāng)我們導(dǎo)入 objc/runtime.h 和 objc/message.h 后珊肃,會發(fā)現(xiàn)代碼提示不全荣刑,原因是從 Xcode5 開始,官方不建議我們手動調(diào)用 Runtime API伦乔±骺鳎可以通過修改 Build Settings 中的一個配置項來解決。
默認(rèn)值為 YES烈和,修改為 NO 即可爱只。
Runtime 的具體應(yīng)用
- 使用關(guān)聯(lián)對象(AssociatedObject)為 Category 添加屬性。
- 遍歷 Class 的所有成員變量(修改 UITextField 的占位文本顏色招刹、字典轉(zhuǎn)模型恬试、自動歸檔解檔等)。
- 交換方法實現(xiàn)(交換系統(tǒng)的方法)疯暑。
- 利用消息轉(zhuǎn)發(fā)機(jī)制解決方法找不到的異常問題训柴。
- ...
isa
- 要了解 Runtime,首先要了解它底層的一些常用數(shù)據(jù)結(jié)構(gòu)妇拯,比如 isa 指針幻馁。
- 在 arm64 架構(gòu)之前,isa 就是一個普通的指針乖阵,存儲著 Class 對象宣赔、Meta-Class 對象的內(nèi)存地址。
- 從 arm64 架構(gòu)開始瞪浸,對 isa 進(jìn)行了優(yōu)化儒将,變成了一個共用體(union),還使用位域來存儲更多信息对蒲。
isa_t 源碼(對 objc_class 的解讀參考這里Objective-C 對象的本質(zhì) 01 - 底層實現(xiàn)):
typedef unsigned long uintptr_t;
union isa_t {
isa_t() { }
isa_t(uintprt_t value) : bits(value) { }
Class cls;
uintptr_t bits;
// 通過掩碼的方式獲取 shiftcls 和 magic 的值钩蚊。
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
struct {
uintptr_t nonpointer : 1;
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 33; // MACH_VM_MAX_ADDRESS 0x1000000000
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 19;
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
};
};
isa 位域
- nonpointer:
- 0:代表普通指針,存儲著 Class 對象蹈矮、Meta-Class 對象的內(nèi)存地址砰逻。
- 1:代表開啟 isa 指針優(yōu)化,使用位域存儲更多的信息泛鸟。
- has_assoc:
- 是否有設(shè)置過關(guān)聯(lián)對象蝠咆,如果沒有,釋放時會更快。
- has_cxx_dtor:
- 是否有 C++ 的析構(gòu)函數(shù)(.cxx_destruct)刚操,如果沒有闸翅,釋放時會更快。
- shiftcls:
- 存儲著 Class 對象菊霜、Meta-Class 對象的內(nèi)存地址坚冀,arm64 架構(gòu)中有 33 位可以存儲地址。
- 根據(jù) ISA_MASK 可以得知鉴逞,Class 對象记某、Meta-Class 對象的內(nèi)存地址值后三位(二進(jìn)制位)永遠(yuǎn)為0。
- magic:
- 用戶判斷對象是否初始化完成构捡,在 arm64 中 0x16 是調(diào)試器判斷當(dāng)前對象是初始化完成的還是沒有初始化的空間液南。
- weakly_referenced:
- 是否有被弱引用指向過,如果沒有叭喜,釋放時會更快贺拣。
- deallocating:
- 對象是否正在釋放內(nèi)存。
- has_sidetable_rc:
- 用于判斷對象的引用計數(shù)是否過大捂蕴,如果 extra_rc 位域無法容納引用計數(shù)譬涡,會存儲到 SideTable 的屬性中。
- extra_rc:
- 存放對象的引用計數(shù)值減一后的結(jié)果啥辨,對象的引用計數(shù)超過 1涡匀,會存儲到這里。
從 objc_destructInstance 函數(shù)中溉知,可以看到對象設(shè)置過關(guān)聯(lián)對象或有 C++ 析構(gòu)函數(shù)陨瘩,會進(jìn)行對應(yīng)的釋放操作。
/***********************************************************************
* objc_destructInstance
* Destroys an instance without freeing memory.
* Calls C++ destructors.
* Calls ARC ivar cleanup.
* Removes associative references.
* Returns `obj`. Does nothing if `obj` is nil.
**********************************************************************/
void *objc_destructInstance(id obj)
{
if (obj) {
// Read all of the flags at once for performance.
bool cxx = obj->hasCxxDtor();
bool assoc = obj->hasAssociatedObjects();
// This order is important.
if (cxx) object_cxxDestruct(obj);
if (assoc) _object_remove_assocations(obj);
obj->clearDeallocating();
}
return obj;
}