使用objc runtime實(shí)現(xiàn)iOS閉環(huán)的懶加載功能
使用objc runtime實(shí)現(xiàn)懶加載
懶加載形式如下
- (id)lazyloadProperty{
if(_lazyloadProperty == nil){
_lazyloadProperty = [XClass ...];
}
return _lazyloadProperty;
}
一般使用宏定義可以輕松完成。但是沒(méi)有一致性,移植差。
利用objc runtime的動(dòng)態(tài)性實(shí)現(xiàn)懶加載可以實(shí)現(xiàn)即可增加又可刪除功能,也可以避免污染類(lèi)型。該三方彌補(bǔ)了目前沒(méi)有閉環(huán)實(shí)現(xiàn)懶加載三方的空缺。
主要流程:
- 實(shí)例或者類(lèi)的懶加載
- 如果是實(shí)例對(duì)象則鉤住并修改類(lèi)型將其子類(lèi)化
- 對(duì)該類(lèi)型進(jìn)行method swizzling
- 如果現(xiàn)在進(jìn)行解綁,則判斷是否是自己實(shí)現(xiàn)的方法.如果是自己實(shí)現(xiàn)的方法->5,否則->6
- 調(diào)用method swizzling還原
- 刪除這個(gè)類(lèi)型的這個(gè)方法
難點(diǎn):
自己實(shí)現(xiàn)method swizzling
- 重新實(shí)現(xiàn)objc1時(shí)代的方法class_removeMethods
- 鉤住運(yùn)行時(shí)中的runtimelock,實(shí)現(xiàn)修改類(lèi)型數(shù)據(jù)時(shí)的安全性
我們?cè)賹?shí)現(xiàn)method swizzling時(shí)的兩個(gè)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);
不管使用哪種,如果這個(gè)類(lèi)型沒(méi)有實(shí)現(xiàn)該方法而是父類(lèi)實(shí)現(xiàn)的話,就需要?jiǎng)討B(tài)增加一個(gè)方法。動(dòng)態(tài)增加的方法在Objc1時(shí)代,是可以通過(guò)下列方法刪除的:
OBJC_EXPORT void class_removeMethods(Class _Nullable, struct objc_method_list * _Nonnull) OBJC2_UNAVAILABLE;
Objc2時(shí)代之后runtime被重寫(xiě)后沒(méi)有該方法了,并且新的runtime的類(lèi)結(jié)構(gòu)看起來(lái)就沒(méi)打算讓開(kāi)發(fā)者刪除方法,所以這里將過(guò)程記下。
首先看類(lèi)讀寫(xiě)器的結(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;//刪除這里的一個(gè)方法
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)。存儲(chǔ)的內(nèi)容是method_list_t.
method_list_t又繼承于entsize_list_tt<method_t, method_list_t, 0x3>,他也是數(shù)組結(jié)構(gòu)。
整個(gè)method_array_t結(jié)構(gòu)是二維數(shù)組。每次刪掉一個(gè)method_t需要用新method_list_t替換原對(duì)象。

然后是線程安全的問(wèn)題,需要獲取到蘋(píng)果在操作類(lèi)型的時(shí)候使用的讀寫(xiě)鎖(pthread_rw_lock_t runtimelock)。沒(méi)有這把鎖任何對(duì)runtime的修改都是不可靠的。
最終采取的方式是:劫持暴露了符號(hào)的系統(tǒng)函數(shù)然后阻塞線程

劫持系統(tǒng)C函數(shù)使用的是臉書(shū)的魚(yú)鉤,這個(gè)鉤子在macOS其實(shí)也是可以正常工作的。
剩下的就是尋找合適的函數(shù)了,這函數(shù)要滿足兩個(gè)條件:
- 該函數(shù)在符號(hào)表中存在
- 函數(shù)內(nèi)部在lock runtimelock之后存在滿足條件1的第二個(gè)函數(shù)
找了半天發(fā)現(xiàn)最合適的只有objc_allocateProtocol()了,objc_allocateProtocol內(nèi)部會(huì)調(diào)用calloc(),所以第二個(gè)被劫持函數(shù)就是calloc。為了減小calloc的開(kāi)銷(xiāo),需要稍微做一些工作。
- 對(duì)每次調(diào)用進(jìn)行比較線程ID的操作顯然比暴力阻塞線程好。
- 減小劫持后的calloc的調(diào)用棧。
總結(jié)
以上所述是小編給大家介紹的使用objc runtime實(shí)現(xiàn)iOS閉環(huán)的懶加載功能,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)歡迎給我留言,小編會(huì)及時(shí)回復(fù)大家的!
相關(guān)文章
iOS bounds學(xué)習(xí)筆記以及仿寫(xiě)UIScrollView部分功能詳解
這篇文章主要為大家詳細(xì)介紹了iOS bounds學(xué)習(xí)筆記,以及仿寫(xiě)UIScrollView部分功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-05-05
iOS開(kāi)發(fā)之UIScrollView控件詳解
UIScrollView是一個(gè)非常重要的控件,其可以展示比設(shè)備屏幕更大區(qū)域的內(nèi)容,我們可以通過(guò)手指滑動(dòng)來(lái)查看內(nèi)容視圖的每一部分內(nèi)容,也可以通過(guò)手指捏合來(lái)對(duì)內(nèi)容視圖進(jìn)行縮放操作,我們每天開(kāi)發(fā)中都不斷顯式或隱式地與UIScrollView打交道,下面給大家詳細(xì)介紹UIScrollView控件。2016-09-09
iOS Crash常規(guī)跟蹤方法及Bugly集成運(yùn)用詳細(xì)介紹
這篇文章主要介紹了iOS Crash常規(guī)跟蹤方法及Bugly集成運(yùn)用詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下2016-10-10
iOS表情鍵盤(pán)的簡(jiǎn)單實(shí)現(xiàn)代碼
這篇文章主要為大家詳細(xì)介紹了iOS表情鍵盤(pán)的簡(jiǎn)單實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03
Objective-C關(guān)鍵字@property使用原理探究
這篇文章主要為大家介紹了Objective-C關(guān)鍵字@property使用原理探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01
iOS應(yīng)用中UILabel文字顯示效果的常用設(shè)置總結(jié)
UILabel組件可以用來(lái)設(shè)置文字內(nèi)容的排版與字體效果等,功能非常多,下面就來(lái)為大家整理一下基本的iOS應(yīng)用中UILabel文字顯示效果的常用設(shè)置總結(jié)2016-05-05
iOS開(kāi)發(fā)之運(yùn)動(dòng)事件和遠(yuǎn)程控制
在iOS中事件分為三類(lèi):觸摸事件:通過(guò)觸摸、手勢(shì)進(jìn)行觸發(fā)(例如手指點(diǎn)擊、縮放),運(yùn)動(dòng)事件:通過(guò)加速器進(jìn)行觸發(fā)(例如手機(jī)晃動(dòng)),遠(yuǎn)程控制事件:通過(guò)其他遠(yuǎn)程設(shè)備觸發(fā)(例如耳機(jī)控制按鈕)今天我們來(lái)詳細(xì)探討下運(yùn)動(dòng)事件和遠(yuǎn)程控制2016-04-04

