本着追本溯源的思想(以及面试官的要求……),结合JSPatch作者对JSPatch原理的解释,说一说个人对JSPatch原理的理解。
1.基于苹果的JavascriptCore框架
JavascriptCore框架是实现JS和OC互相交互的框架,使用这个框架,你可以在OC里面调用JS代码,也可以在JS中调用OC的代码。这个框架是JSPatch实现的基础。关于JavascriptCore框架,可以查看这篇文章。
2.基础原理是OC的动态语言特性
使用JSPatch来进行热修复,在很多情况下我们都是通过替换方法来实现的,在OC中,利用runtime可以很容易做到这一点。
3.实现方法调用
当使用require
方法引入一个类的时候,JSPatch实际做的是在全局作用域的某个全局对象里,创建一个对象:
{
__clsName: "UIView"
}
当使用这个类,调用类方法的时候,就会去全局对象里取这个对象。现在这个对象还没有方法,而在JS中调用某个对象没有的方法会抛出异常。这里JSPatch作者的解决方法是给Object对象定义一个__c()
方法,而在OC那边,JSEngine实际使用JavascriptCore框架执行JS代码之前,将所有的方法调用改为调用__c()
方法,这个方法内部做的就是将方法名参数等信息传到OC,去OC里通过runtime执行,这样一来就不会崩溃了。
除了这个问题以外,JSPatch还对OC回传的对象做了包装,通过_obj
这个key把他包装:
{__obj: [OC Object 对象指针]}
通过判断对象是否有 __obj 属性得知这个对象是否表示 OC 对象指针,在 __c 函数里若判断到调用者有 __obj 属性,取出这个属性,跟调用的实例方法一起传回给 OC,就完成了实例方法的调用。
4.方法替换
JSPatch内部的方法替换是通过动态方法解析实现的,用的是forwardInvocation方法,因为可以从NSInvocation里面取得原方法的所有参数(通过va_list取得类结构体的方法列表获取参数在arm64下不可用)。
JSPatch是在应用的didFinishLaunchWithOptions
里执行的,所以方法替换也是在这里实现,它会将原方法的IMP指向_objc_msgForward
(这个方法会执行forwardInvocation),新添加一个方法指向原方法的IMP。当原方法被调用的时候,会直接走forwardInvocation,在这里可以直接调用JS中的替换方法,至于JS的方法调用看上一部分就行了。
网友评论