使用objc runtime實現懶加載git
地址:AutoPropertyCocoagithub
本文所指懶加載形式以下數組
- (id)lazyloadProperty{ if(_lazyloadProperty == nil){ _lazyloadProperty = [XClass ...]; } return _lazyloadProperty; }
通常使用宏定義能夠輕鬆完成。可是沒有一致性,移植差。安全
利用objc runtime的動態性實現懶加載能夠實現即可增長又可刪除功能,也能夠避免污染類型。該三方彌補了目前沒有閉環實現懶加載三方的空缺。框架
主要實現內容:函數
難點:ui
本身實現method swizzlingthis
咱們再實現method swizzling時的兩個APIspa
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);
無論使用哪一種,若是這個類型沒有實現該方法而是父類實現的話,就須要動態增長一個方法。動態增長的方法在Objc1時代,是能夠經過下列方法刪除的:線程
OBJC_EXPORT void class_removeMethods(Class _Nullable, struct objc_method_list * _Nonnull) OBJC2_UNAVAILABLE;
Objc2時代以後runtime被重寫後沒有該方法了,而且新的runtime的類結構看起來就沒打算讓開發者刪除方法,因此這裏將過程記下。
首先看類讀寫器的結構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>,它是數組結構。存儲的內容是method_list_t.
method_list_t又繼承於entsize_list_tt<method_t, method_list_t, 0x3>,他也是數組結構。
整個method_array_t結構是二維數組。每次刪掉一個method_t須要用新method_list_t替換原對象。
而後是線程安全的問題,須要獲取到蘋果在操做類型的時候使用的讀寫鎖(pthread_rw_lock_t runtimelock)。沒有這把鎖任何對runtime的修改都是不可靠的。
最終採起的方式是:劫持暴露了符號的系統函數而後阻塞線程
劫持系統C函數使用的是臉書的魚鉤,這個鉤子在macOS其實也是能夠正常工做的。
剩下的就是尋找合適的函數了,這函數要知足兩個條件:
找了半天發現最合適的只有objc_allocateProtocol()了,objc_allocateProtocol內部會調用calloc(),因此第二個被劫持函數就是calloc。爲了減少calloc的開銷,須要稍微作一些工做。
雖然不是什麼吸引人的UI框架仍是但願你們點個贊吧。另外,有成都的公司招iOS嗎😂。
Email : meterwhite@outlook.com