使用objc runtime實現(xiàn)懶加載 本文所指懶加載形式如下 - (id)lazyloadProperty{ if(_lazyloadProperty == nil){ _lazyloadProperty = [XClass ...]; } return _lazyloadProperty; } 一般使用宏定義可以輕松完成。但是沒有一致性,移植差。 利用objc runtime的動態(tài)性實現(xiàn)懶加載可以實現(xiàn)即可增加又可刪除功能,也可以避免污染類型。該三方彌補了目前沒有閉環(huán)實現(xiàn)懶加載三方的空缺。
主要實現(xiàn)內(nèi)容:
難點: 自己實現(xiàn)method swizzling
我們再實現(xiàn)method swizzling時的兩個API OBJC_EXPORT IMP _Nullable class_replaceMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, const char * _Nullable types) OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0); OBJC_EXPORT void method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2) OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0); 不管使用哪種,如果這個類型沒有實現(xiàn)該方法而是父類實現(xiàn)的話,就需要動態(tài)增加一個方法。動態(tài)增加的方法在Objc1時代,是可以通過下列方法刪除的: OBJC_EXPORT void class_removeMethods(Class _Nullable, struct objc_method_list * _Nonnull) OBJC2_UNAVAILABLE; Objc2時代之后runtime被重寫后沒有該方法了,并且新的runtime的類結(jié)構(gòu)看起來就沒打算讓開發(fā)者刪除方法,所以這里將過程記下。 首先看類讀寫器的結(jié)構(gòu)class_rw_t struct class_rw_t { // Be warned that Symbolication knows the layout of this structure. uint32_t flags; uint32_t version; const class_ro_t *ro; method_array_t methods;//刪除這里的一個方法 property_array_t properties; protocol_array_t protocols; Class firstSubclass; Class nextSiblingClass; char *demangledName; #if SUPPORT_INDEXED_ISA uint32_t index; #endif }; method_array_t繼承于list_array_tt<method_t, method_list_t>,它是數(shù)組結(jié)構(gòu)。存儲的內(nèi)容是method_list_t. method_list_t又繼承于entsize_list_tt<method_t, method_list_t, 0x3>,他也是數(shù)組結(jié)構(gòu)。 整個method_array_t結(jié)構(gòu)是二維數(shù)組。每次刪掉一個method_t需要用新method_list_t替換原對象。 然后是線程安全的問題,需要獲取到蘋果在操作類型的時候使用的讀寫鎖(pthread_rw_lock_t runtimelock)。沒有這把鎖任何對runtime的修改都是不可靠的。 最終采取的方式是:劫持暴露了符號的系統(tǒng)函數(shù)然后阻塞線程 劫持系統(tǒng)C函數(shù)使用的是臉書的魚鉤,這個鉤子在macOS其實也是可以正常工作的。 剩下的就是尋找合適的函數(shù)了,這函數(shù)要滿足兩個條件:
找了半天發(fā)現(xiàn)最合適的只有objc_allocateProtocol()了,objc_allocateProtocol內(nèi)部會調(diào)用calloc(),所以第二個被劫持函數(shù)就是calloc。為了減小calloc的開銷,需要稍微做一些工作。
雖然不是什么吸引人的UI框架還是希望大家點個贊吧。另外,有成都的公司招iOS嗎 |
|