原理基础
- JSPatch 之所以能做到通过 JS 调用和改写 OC 方法最根本的原因是 Objective-C 是一门动态语言,OC 上所有方法的调用/类的生成都是通过 Objective-C Runtime 在运行时进行。JS 传递字符串给 OC,OC 通过 Runtime 接口调用和替换 OC 方法。
通过类名/方法名反射得到相应的类和方法
Class class = NSClassFromString("UIViewController");
id viewController = [[class alloc] init];
SEL selector = NSSelectorFromString("viewDidLoad");
[viewController performSelector:selector];
替换某个类的方法为新的实现
static void newViewDidLoad(id slf, SEL sel) {}
class_replaceMethod(class, selector, newViewDidLoad, @"");
甚至可以新注册一个类,为类添加方法
Class cls = objc_allocateClassPair(superCls, "JPObject", 0);
objc_registerClassPair(cls);
class_addMethod(cls, selector, implement, typedesc);
注:Runtime是开源的
框架图
<br />
通过下面一段简单的代码来解释以下上面这个框架图的含义
require('UIView')
var view = UIView.alloc().init()
view.setBackgroundColor(require('UIColor').grayColor())
view.setAlpha(0.5)
require其实就是下面这么一个js函数
var _require = function(clsName) {
if (!global[clsName]) {
global[clsName] = {
__isCls: 1,
__clsName: clsName
}
}
return global[clsName]
}
它在全局作用域生成了 UIView 这个变量,指向一个这样一个对象:
{
__isCls: 1,
__clsName: "UIView"
}
下面来看看UIView.alloc().init()
是怎么被oc执行到的
- 执行之前会该段js代码会被替换如下:
UIView.alloc().init()
->
UIView.__c('alloc')().__c('init')()
- 我们看到
__c()
这个函数,他是哪来的呢,干什么的呢?我觉得作者的这一步的算是这个框架的点睛之笔了,__c()
函数原型是这样的:
Object.prototype.__c = function(methodName) {
if (!this.__obj && !this.__clsName)
return this[methodName].bind(this);
var self = this
return function(){
var args = Array.prototype.slice.call(arguments)
return _methodFunc(self.__obj, self.__clsName, methodName, args, self.__isSuper)
}
}
- Object是JS对象的基类,给Object的prototype加上
__c
成员,这样所有对象都可以调用到__c
-
_methodFunc
这个函数的作用就是把相关信息传给OC,OC用 Runtime接口调用相应方法并返回结果值
到此js->oc的这条路就走通了,JSPatch,接下来可能就是一些Runtime上细节的处理了,比如:消息传递
,对象持有/转换
,类型转换
,方法新增
,发放替换
,self关键字
,super关键字
等等这些的实现,其中主要的技术手段都是Objective-Runtime(这是是开源的可以看看)。