本著追本溯源的思想(以及面試官的要求……)帐姻,結(jié)合JSPatch作者對(duì)JSPatch原理的解釋祟印,說(shuō)一說(shuō)個(gè)人對(duì)JSPatch原理的理解。
1.基于蘋果的JavascriptCore框架
JavascriptCore框架是實(shí)現(xiàn)JS和OC互相交互的框架鳄梅,使用這個(gè)框架巷送,你可以在OC里面調(diào)用JS代碼,也可以在JS中調(diào)用OC的代碼擂仍。這個(gè)框架是JSPatch實(shí)現(xiàn)的基礎(chǔ)府怯。關(guān)于JavascriptCore框架,可以查看這篇文章防楷。
2.基礎(chǔ)原理是OC的動(dòng)態(tài)語(yǔ)言特性
使用JSPatch來(lái)進(jìn)行熱修復(fù)牺丙,在很多情況下我們都是通過(guò)替換方法來(lái)實(shí)現(xiàn)的,在OC中复局,利用runtime可以很容易做到這一點(diǎn)冲簿。
3.實(shí)現(xiàn)方法調(diào)用
當(dāng)使用require
方法引入一個(gè)類的時(shí)候,JSPatch實(shí)際做的是在全局作用域的某個(gè)全局對(duì)象里亿昏,創(chuàng)建一個(gè)對(duì)象:
{
__clsName: "UIView"
}
當(dāng)使用這個(gè)類峦剔,調(diào)用類方法的時(shí)候,就會(huì)去全局對(duì)象里取這個(gè)對(duì)象〗枪常現(xiàn)在這個(gè)對(duì)象還沒(méi)有方法吝沫,而在JS中調(diào)用某個(gè)對(duì)象沒(méi)有的方法會(huì)拋出異常。這里JSPatch作者的解決方法是給Object對(duì)象定義一個(gè)__c()
方法递礼,而在OC那邊惨险,JSEngine實(shí)際使用JavascriptCore框架執(zhí)行JS代碼之前,將所有的方法調(diào)用改為調(diào)用__c()
方法脊髓,這個(gè)方法內(nèi)部做的就是將方法名參數(shù)等信息傳到OC辫愉,去OC里通過(guò)runtime執(zhí)行,這樣一來(lái)就不會(huì)崩潰了将硝。
除了這個(gè)問(wèn)題以外恭朗,JSPatch還對(duì)OC回傳的對(duì)象做了包裝屏镊,通過(guò)_obj
這個(gè)key把他包裝:
{__obj: [OC Object 對(duì)象指針]}
通過(guò)判斷對(duì)象是否有 __obj 屬性得知這個(gè)對(duì)象是否表示 OC 對(duì)象指針,在 __c 函數(shù)里若判斷到調(diào)用者有 __obj 屬性痰腮,取出這個(gè)屬性而芥,跟調(diào)用的實(shí)例方法一起傳回給 OC,就完成了實(shí)例方法的調(diào)用膀值。
4.方法替換
JSPatch內(nèi)部的方法替換是通過(guò)動(dòng)態(tài)方法解析實(shí)現(xiàn)的棍丐,用的是forwardInvocation方法,因?yàn)榭梢詮腘SInvocation里面取得原方法的所有參數(shù)(通過(guò)va_list取得類結(jié)構(gòu)體的方法列表獲取參數(shù)在arm64下不可用)虫腋。
JSPatch是在應(yīng)用的didFinishLaunchWithOptions
里執(zhí)行的骄酗,所以方法替換也是在這里實(shí)現(xiàn),它會(huì)將原方法的IMP指向_objc_msgForward
(這個(gè)方法會(huì)執(zhí)行forwardInvocation)悦冀,新添加一個(gè)方法指向原方法的IMP趋翻。當(dāng)原方法被調(diào)用的時(shí)候,會(huì)直接走forwardInvocation盒蟆,在這里可以直接調(diào)用JS中的替換方法踏烙,至于JS的方法調(diào)用看上一部分就行了。