前言

JSPatch是一个可以动态更新iOS APP的开源库。通过JSPatch,可以使用JS书写原生代码,动态更新APP,替换项目原生代码修复Bug。

JSPatch充分应用了Objective-C的runtime,来实现自己的功能。

这篇文章将结合Demo,来分析JSPatch的主要的实现过程。包括:

  1. 使用JS新建类
  2. 使用JS替换原有方法
  3. 使用JS添加全新方法
  4. OC调用JS定义的方法
  5. JS调用OC定义的方法

Demo

使用官方提供的Demo。先来看看Demo的关键代码:

JPViewController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@implementation JPViewController

- (void)viewDidLoad {
[super viewDidLoad];
UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(0, 100, [UIScreen mainScreen].bounds.size.width, 50)];
[btn setTitle:@"Push JPTableViewController" forState:UIControlStateNormal];
[btn addTarget:self action:@selector(handleBtn:) forControlEvents:UIControlEventTouchUpInside];
[btn setBackgroundColor:[UIColor grayColor]];
[self.view addSubview:btn];
}

- (void)handleBtn:(id)sender
{
}

@end

这个JPViewController创建了一个button,并给该button赋予了一个点击事件。但是该点击事件- (void)handleBtn:(id)sender;没做任何事情

demo.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/* 给JPViewController重新定义 handleBtn: 方法 */
defineClass('JPViewController', {
handleBtn: function(sender) {
var tableViewCtrl = JPTableViewController.alloc().init()
self.navigationController().pushViewController_animated(tableViewCtrl, YES)
}
})

// 定义了一个新的类型 JPTableViewController
defineClass('JPTableViewController : UITableViewController <UIAlertViewDelegate>', ['data'], {
dataSource: function() {
var data = self.data();
if (data) return data;
var data = [];
for (var i = 0; i < 20; i ++) {
data.push("cell from js " + i);
}
self.setData(data)
return data;
},
numberOfSectionsInTableView: function(tableView) {
return 1;
},
tableView_numberOfRowsInSection: function(tableView, section) {
return self.dataSource().length;
},
tableView_cellForRowAtIndexPath: function(tableView, indexPath) {
var cell = tableView.dequeueReusableCellWithIdentifier("cell")
if (!cell) {
cell = require('UITableViewCell').alloc().initWithStyle_reuseIdentifier(0, "cell")
}
cell.textLabel().setText(self.dataSource()[indexPath.row()])
return cell
},
tableView_heightForRowAtIndexPath: function(tableView, indexPath) {
return 60
},
tableView_didSelectRowAtIndexPath: function(tableView, indexPath) {
var alertView = require('UIAlertView').alloc().initWithTitle_message_delegate_cancelButtonTitle_otherButtonTitles("Alert",self.dataSource()[indexPath.row()], self, "OK", null);
alertView.show()
},
alertView_willDismissWithButtonIndex: function(alertView, idx) {
console.log('click btn ' + alertView.buttonTitleAtIndex(idx).toJS())
}
})

该JS是Demo的唯一一个JS。它做了两件事:

  1. 重新定义JPViewControllerhandleBtn:方法;现在点击该button的话,会新建一个tableView
  2. 定义一个新的类型 JPTableViewController;这个新的类型负责创建相关的tableView

JPEngine.m 和 JSPatch.js

这是JSPatch库的最核心的两个文件。

该Demo创建了一个button,点击button之后,会调用JS里面的方法:该JS方法会创建一个tableView,并给每个cell设置一个click事件

源码分析

使用JS创建Objective-C的类

demo.js中使用了 defineClass 方法来创建新的Objective-C类。这个方法定义在JSPatch.js中。现在来看看它的实现:(只显示了核心代码)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
global.defineClass = function(declaration, properties, instMethods, clsMethods) {
var newInstMethods = {}, newClsMethods = {}

// 获得类名,忽略冒号之后的superClass以及protocol //
var realClsName = declaration.split(':')[0].trim()

// JS <=> OC 的一些格式化 暂时不管,这一步之后将会产生
_formatDefineMethods(instMethods, newInstMethods, realClsName)
_formatDefineMethods(clsMethods, newClsMethods, realClsName)

// 核心代码: 在_OC_defineClass中调用OC的方法,创建新的Objective-C类,并添加或者替代方法
var ret = _OC_defineClass(declaration, newInstMethods, newClsMethods)

/* 此处省略800字 */
return require(className)
}

使用iOS7引入的JavaScriptCore框架 ,可以实现在JS中调用OC的方法(具体参考JavaScriptCore)。defineClass方法中,调用_OC_defineClass方法来创建新的类,以及替换原有方法或者生成新的方法。

来看看定义在JPEngine.m文件中的这个方法:

1
2
3
4
5
+ (void)startEngine {
context[@"_OC_defineClass"] = ^(NSString *classDeclaration, JSValue *instanceMethods, JSValue *classMethods) {
return defineClass(classDeclaration, instanceMethods, classMethods);
};
}

这里的context 变量是JSContext变量,也就是JS的执行环境;在该执行环境里面定义了一个_OC_defineClass Block。那么JS就可以调用该_OC_defineClass方法,并执行Block,也就是执行OC代码。

来看看Block里面的defineClass这一个C方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
static NSDictionary *defineClass(NSString *classDeclaration, JSValue *instanceMethods, JSValue *classMethods)
{
// 这里的classDeclaration类似这种格式 : ClassName : NSObject <protocolName>
NSScanner *scanner = [NSScanner scannerWithString:classDeclaration];

NSString *className;
NSString *superClassName;
NSString *protocolNames;

/* 此处省略800字 解析生成className superClassName 和 protocolName*/

Class cls = NSClassFromString(className);

// 如果OC中不存在这个类 则新建class
if (!cls) {
Class superCls = NSClassFromString(superClassName);
cls = objc_allocateClassPair(superCls, className.UTF8String, 0);
objc_registerClassPair(cls);
}

// 给该类添加协议
if (protocols.count > 0) {
for (NSString* protocolName in protocols) {
Protocol *protocol = objc_getProtocol([trim(protocolName) cStringUsingEncoding:NSUTF8StringEncoding]);
class_addProtocol (cls, protocol);
}
}

/* 此处省略800字 */
}

总结: 可以看到,在JS中创建的类名会被传入OC。OC会判断该类名对应的类是否存在;如果不存在,就会使用runtime方法来动态创建这个类

使用JS替换原有方法

再来看一下demo.js

1
2
3
4
5
6
defineClass('JPViewController', {
handleBtn: function(sender) {
var tableViewCtrl = JPTableViewController.alloc().init()
self.navigationController().pushViewController_animated(tableViewCtrl, YES)
}
})

这里使用JS定义了JPViewController类的handleBtn:方法;执行完defineClass这个JS方法之后,这里的实现会替换原来的handleBtn:的实现。

先来说一下方法替换的具体流程:

  1. 创建一个ORIGhandleBtn: selector,指向handleBtn:的IMP
  2. 创建一个ORIGforwardInvocation: selector,指向JPViewControllerforwardInvocation: 的IMP
  3. handleBtn: selector 指向 _objc_msgForward ;也就是说执行handleBtn:这个方法的时候,不会去查找它的实现,会直接进行消息转发(关于消息转发,可以看我的这篇文章 Objective-C runtime - 消息)
  4. 在OC的全局变量 _JSOverideMethods 中保存这个方法的实现,也就是保存JS中定义的这个方法
  5. forwardInvocation: 这个selector指向 JPForwardInvocation的实现;**JPForwardInvocation又是JSPatch里面的一个核心方法**。在这个方法里面会寻找JS定义的handleBtn,并调用该方法

来看看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
static NSDictionary *defineClass(NSString *classDeclaration, JSValue *instanceMethods, JSValue *classMethods)
{
/* 此处省略800字 */

// i == 0 添加实例方法; i == 1 添加类方法
for (int i = 0; i < 2; i ++) {
BOOL isInstance = i == 0;
JSValue *jsMethods = isInstance ? instanceMethods: classMethods;

Class currCls = isInstance ? cls: objc_getMetaClass(className.UTF8String);

// 获取方法列表,for循环逐个给类添加方法
NSDictionary *methodDict = [jsMethods toDictionary];
for (NSString *jsMethodName in methodDict.allKeys) {
JSValue *jsMethodArr = [jsMethods valueForProperty:jsMethodName];
int numberOfArg = [jsMethodArr[0] toInt32];
NSString *selectorName = convertJPSelectorString(jsMethodName);
if ([selectorName componentsSeparatedByString:@":"].count - 1 < numberOfArg) {
selectorName = [selectorName stringByAppendingString:@":"];
}

JSValue *jsMethod = jsMethodArr[1];
if (class_respondsToSelector(currCls, NSSelectorFromString(selectorName))) {
// 已经有对应的方法了,替换方法
overrideMethod(currCls, selectorName, jsMethod, !isInstance, NULL);
} else {
// 新增方法
/* 先省略800字 后文再说 */
}
}
}

// 添加统一的setter/getter函数
class_addMethod(cls, @selector(getProp:), (IMP)getPropIMP, "@@:@");
class_addMethod(cls, @selector(setProp:forKey:), (IMP)setPropIMP, "v@:@@");

return @{@"cls": className, @"superCls": superClassName};
}

看来替换方法的核心还在overrideMethod 里面,来看看这个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
static void overrideMethod(Class cls, NSString *selectorName, JSValue *function, BOOL isClassMethod, const char *typeDescription)
{
// 获取方法名对应的selector
SEL selector = NSSelectorFromString(selectorName);

// 获取method的typeDescription:也就是获取方法的typeEncoding
if (!typeDescription) {
Method method = class_getInstanceMethod(cls, selector);
typeDescription = (char *)method_getTypeEncoding(method);
}

// 获取方法的最初的实现
IMP originalImp = class_respondsToSelector(cls, selector) ? class_getMethodImplementation(cls, selector) : NULL;

// 获取_objc_msgForward的实现
IMP msgForwardIMP = _objc_msgForward;

// 保留forwardInvocation:的原有实现,并将它指向JPForwardInvocation这个新的实现上面
if (class_getMethodImplementation(cls, @selector(forwardInvocation:)) != (IMP)JPForwardInvocation) {
IMP originalForwardImp = class_replaceMethod(cls, @selector(forwardInvocation:), (IMP)JPForwardInvocation, "v@:@");
if (originalForwardImp) {
class_addMethod(cls, @selector(ORIGforwardInvocation:), originalForwardImp, "v@:@");
}
}

// 保留将要替换的方法:使用ORIGselectorName这一个selector指向原有的实现
if (class_respondsToSelector(cls, selector)) {
NSString *originalSelectorName = [NSString stringWithFormat:@"ORIG%@", selectorName];
SEL originalSelector = NSSelectorFromString(originalSelectorName);
if(!class_respondsToSelector(cls, originalSelector)) {
class_addMethod(cls, originalSelector, originalImp, typeDescription);
}
}

// 将JS定义的实现保存到OC的全局变量中
NSString *JPSelectorName = [NSString stringWithFormat:@"_JP%@", selectorName];
_initJPOverideMethods(cls);
_JSOverideMethods[cls][JPSelectorName] = function;

// 将要替换的方法,指向_objc_msgForward这一函数,直接进行消息转发
class_replaceMethod(cls, selector, msgForwardIMP, typeDescription);
}

使用JS创建新的方法

还是先来看看demo.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
defineClass('JPTableViewController : UITableViewController <UIAlertViewDelegate>', ['data'], {
dataSource: function() {
var data = self.data();
if (data) return data;
var data = [];
for (var i = 0; i < 20; i ++) {
data.push("cell from js " + i);
}
self.setData(data)
return data;
},
numberOfSectionsInTableView: function(tableView) {
return 1;
},
tableView_numberOfRowsInSection: function(tableView, section) {
return self.dataSource().length;
},
tableView_cellForRowAtIndexPath: function(tableView, indexPath) {
var cell = tableView.dequeueReusableCellWithIdentifier("cell")
if (!cell) {
cell = require('UITableViewCell').alloc().initWithStyle_reuseIdentifier(0, "cell")
}
cell.textLabel().setText(self.dataSource()[indexPath.row()])
return cell
},
tableView_heightForRowAtIndexPath: function(tableView, indexPath) {
return 60
},
tableView_didSelectRowAtIndexPath: function(tableView, indexPath) {
var alertView = require('UIAlertView').alloc().initWithTitle_message_delegate_cancelButtonTitle_otherButtonTitles("Alert",self.dataSource()[indexPath.row()], self, "OK", null);
alertView.show()
},
alertView_willDismissWithButtonIndex: function(alertView, idx) {
console.log('click btn ' + alertView.buttonTitleAtIndex(idx).toJS())
}
})

在这个JS方法里面,不仅定义了一个新的OC类JPTableViewController,并给他添加了多个方法。来看看具体实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
static NSDictionary *defineClass(NSString *classDeclaration, JSValue *instanceMethods, JSValue *classMethods)
{
/* 上文提过,省略 */

// i == 0 添加实例方法; i == 1 添加类方法
for (int i = 0; i < 2; i ++) {
BOOL isInstance = i == 0;
JSValue *jsMethods = isInstance ? instanceMethods: classMethods;

Class currCls = isInstance ? cls: objc_getMetaClass(className.UTF8String);
NSDictionary *methodDict = [jsMethods toDictionary];
for (NSString *jsMethodName in methodDict.allKeys) {
JSValue *jsMethodArr = [jsMethods valueForProperty:jsMethodName];
int numberOfArg = [jsMethodArr[0] toInt32];
NSString *selectorName = convertJPSelectorString(jsMethodName);
if ([selectorName componentsSeparatedByString:@":"].count - 1 < numberOfArg) {
selectorName = [selectorName stringByAppendingString:@":"];
}

JSValue *jsMethod = jsMethodArr[1];
if (class_respondsToSelector(currCls, NSSelectorFromString(selectorName))) {
overrideMethod(currCls, selectorName, jsMethod, !isInstance, NULL);
} else {
/* 开始新增方法 */
BOOL overrided = NO;

// 先添加protocol的方法
for (NSString *protocolName in protocols) {
// 通过protocol获取方法的typeEncoding
char *types = methodTypesInProtocol(protocolName, selectorName, isInstance, YES);
if (!types) types = methodTypesInProtocol(protocolName, selectorName, isInstance, NO);
if (types) {
// 还是使用 overrideMethod 方法来创建新的方法。走的是消息转发的那一套
overrideMethod(currCls, selectorName, jsMethod, !isInstance, types);
free(types);
overrided = YES;
break;
}
}

// 不是protocol方法,新增类的方法
if (!overrided) {
if (![[jsMethodName substringToIndex:1] isEqualToString:@"_"]) {
NSMutableString *typeDescStr = [@"@@:" mutableCopy];
for (int i = 0; i < numberOfArg; i ++) {
[typeDescStr appendString:@"@"];
}
overrideMethod(currCls, selectorName, jsMethod, !isInstance, [typeDescStr cStringUsingEncoding:NSUTF8StringEncoding]);
}
}
}
}
}

/* 省略无关代码 */

return @{@"cls": className, @"superCls": superClassName};
}

可以看到:新增方法其实和替换方法是一个原理,走的都是消息转发那一套;但是新增方法没有必要保留原有方法的实现,因为本来就不存在原有的方法

OC调用JS定义的方法

现在JS已经定义好了需要的类,以及需要的方法了。那么何时来调用这些方法,以及如何调用呢?

回顾一下JPViewController.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#import "JPViewController.h"

@implementation JPViewController

- (void)viewDidLoad {
[super viewDidLoad];
UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(0, 100, [UIScreen mainScreen].bounds.size.width, 50)];
[btn setTitle:@"Push JPTableViewController" forState:UIControlStateNormal];
[btn addTarget:self action:@selector(handleBtn:) forControlEvents:UIControlEventTouchUpInside];
[btn setBackgroundColor:[UIColor grayColor]];
[self.view addSubview:btn];
}

- (void)handleBtn:(id)sender
{
}

@end

这个文件很简单:创建一个button,给button添加一个执行方法handleBtn: 。那么当运行APP,并点击这一button的时候;会去调用handleBtn:方法。但是由上面的分析可知:handleBtn:方法已经指向_objc_msgForward,也就是说会对这一方法直接进行转发,调用forwardInvocation:方法。下面来看看这个JPForwardInvocation方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
static void JPForwardInvocation(__unsafe_unretained id assignSlf, SEL selector, NSInvocation *invocation)
{
// 根据Invocation,来获取方法的参数信息等等
BOOL deallocFlag = NO;
id slf = assignSlf;
NSMethodSignature *methodSignature = [invocation methodSignature];
NSInteger numberOfArguments = [methodSignature numberOfArguments];
NSString *selectorName = NSStringFromSelector(invocation.selector);
NSString *JPSelectorName = [NSString stringWithFormat:@"_JP%@", selectorName];

// 从全局JS方法列表中获取当前的JS方法
JSValue *jsFunc = getJSFunctionInObjectHierachy(slf, JPSelectorName);

// JS 没有定义该方法 那么就走原始的转发方法
if (!jsFunc) {
JPExecuteORIGForwardInvocation(slf, selector, invocation);
return;
}

NSMutableArray *argList = [[NSMutableArray alloc] init];
/* 省略800字 */

// 从Invocation中根据method的 type Encoding 来获取参数列表,并将其添置argList数组中
for (NSUInteger i = 2; i < numberOfArguments; i++) {
const char *argumentType = [methodSignature getArgumentTypeAtIndex:i];

// 根据type Encoding来添加相应的参数。r代表const
switch(argumentType[0] == 'r' ? argumentType[1] : argumentType[0]) {
/* 此处省略不只800字。。 具体实现看源码 */

// for example
case '@':
// 这个参数是一个OC对象,所以需要往argList中添加对象
id arg;
[invocation getArgument:&arg atIndex:i];
[argList addObject:arg];
break;
case ':':
// 添加selector对应的NSString
}
}

/* 此处省略800字 */

// 参数列表,将OC对象转化成JS可以使用的对象
NSArray *params = _formatOCToJSList(argList);

// 保存返回值的类型
char returnType[255];
strcpy(returnType, [methodSignature methodReturnType]);

// 根据返回类型来调用JS函数
switch (returnType[0] == 'r' ? returnType[1] : returnType[0]) {
/* 此处省略不只800字 这一块是该函数的核心代码 下面只说主要过程 */

// jsval用来存储JS调用的返回值
JSValue *jsval;

// JS调用时单线程,需要加锁
[_JSMethodForwardCallLock lock];

// !!! 核心代码出现啦 OC调用JS定义的方法 !!! */
jsval = [jsFunc callWithArguments:params];
[_JSMethodForwardCallLock unlock];

// 检查JS方法的返回值,看是否需要执行回调函数
while (![jsval isNull] && ![jsval isUndefined] && [jsval hasProperty:@"__isPerformInOC"]) {
NSArray *args = nil;
JSValue *cb = jsval[@"cb"];
if ([jsval hasProperty:@"sel"]) {
id callRet = callSelector(![jsval[@"clsName"] isUndefined] ? [jsval[@"clsName"] toString] : nil, [jsval[@"sel"] toString], jsval[@"args"], ![jsval[@"obj"] isUndefined] ? jsval[@"obj"] : nil, NO);
args = @[[_context[@"_formatOCToJS"] callWithArguments:callRet ? @[callRet] : _formatOCToJSList(@[_nilObj])]];
}
[_JSMethodForwardCallLock lock];
// 执行回调函数,该回调函数也是在JS中定义的
jsval = [cb callWithArguments:args];
[_JSMethodForwardCallLock unlock];
}

// 设置消息转发的调用结果,也就是设置 invocation 的 returnValue
[invocation setReturnValue:&jsval];
}

/* 此处省略800字 和对象释放相关的 */
}

了解了这个消息转发的过程之后,就会知道OC调用JS定义的方法是在JSForwardInvocation中。它回去查找定义在OC里面的存储JS方法的全局变量,找到该JS方法,并执行。

JS调用OC的方法

回顾一下demo.js的代码:

1
2
3
4
5
6
defineClass('JPViewController', {
handleBtn: function(sender) {
var tableViewCtrl = JPTableViewController.alloc().init()
self.navigationController().pushViewController_animated(tableViewCtrl, YES)
}
})

当OC调用handleBtn:方法时,实际上执行的是这个JS定义的方法。在这个JS方法里面会创建一个JPTableViewController对象,并将该对象显示出来。来看看JS如何调用OC的代码:

1
2
3
4
5
6
7
8
9
__c: function(methodName) {
/* 省略一系列迷人的JS操作 */

return function(){
var args = Array.prototype.slice.call(arguments)
// 获取调用方法的类名,调用方法的对象,调用的方法的参数,并且执行这个 _methodFunc 方法
return _methodFunc(slf.__obj, slf.__clsName, methodName, args, slf.__isSuper)
}
},

在JS里面调用OC方法:如 UIView.alloc().init() 都会被转化成这种格式 UIView.__c('alloc')().__c('init')()。也就是调用上述的__c方法。

__c()方法也就是获取调用该方法的类名,对象;以及该方法的参数。并将这些全部传给_methodFunc()方法。所以说__c()方法有点像OC的objc_megSend(id self, selector, ...),它只是一个转发器。

真正调用OC的JS方法是这个_methodFunc(定义在JSPatch.js中):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var _methodFunc = function(instance, clsName, methodName, args, isSuper, isPerformSelector) {
// 做一些准备活动:将JS方法名转成相应的OC方法名(驼峰命名)
var selectorName = methodName
if (!isPerformSelector) {
methodName = methodName.replace(/__/g, "-")
selectorName = methodName.replace(/_/g, ":").replace(/-/g, "_")
var marchArr = selectorName.match(/:/g)
var numOfArgs = marchArr ? marchArr.length : 0
if (args.length > numOfArgs) {
selectorName += ":"
}
}

// 使用_OC_callI / _OC_callC 来调用OC的方法。传递给这两方法的参数是对象,参数,selector等等
var ret = instance ? _OC_callI(instance, selectorName, args, isSuper):
_OC_callC(clsName, selectorName, args)
return _formatOCToJS(ret)
}

我们以_OC_callI为例 (定义在JPEngine.m中):

1
2
3
context[@"_OC_callI"] = ^id(JSValue *obj, NSString *selectorName, JSValue *arguments, BOOL isSuper) {
return callSelector(nil, selectorName, arguments, obj, isSuper);
};

在OC中,这个方法接收obj,selectorName,arguments等参数;实际调用的callSelector方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
static id callSelector(NSString *className, NSString *selectorName, JSValue *arguments, JSValue *instance, BOOL isSuper)
{
/* 省略800字 做一些前期准备:获取类,selector,格式化参数列表等等 */

/* 再省略800字 设置Invocation的target 这个调用还是以消息转发的形式来的 */
[invocation setTarget:instance];

// 和OC调用JS一样,set Invocation的调用参数
// 注意:在OC调用JS中,这里是 get Invocation的参数
for (NSUInteger i = 2; i < numberOfArguments; i++) {
const char *argumentType = [methodSignature getArgumentTypeAtIndex:i];
id valObj = argumentsObj[i-2];
switch (argumentType[0] == 'r' ? argumentType[1] : argumentType[0]) {
// 代码省略了
[invocation setArgument:&value atIndex:i];
}
}

// 执行OC代码,也就是设置好这一个invocation之后,invoke它
[invocation invoke];

char returnType[255];
strcpy(returnType, [methodSignature methodReturnType]);

// 使用returnValue来存储Invocation返回值
id returnValue;
[invocation getReturnValue:&result];

// 根据返回值类型来创建returnValue,并返回。 for example
returnValue = (__bridge id)result;
return returnValue;
}

这样就实现了OC代码的调用。

JSPatch核心方法

JPEngine.m

  1. defineClass
  2. overrideMethod
  3. JPForwardInvocation
  4. callSelector

JSPatch.js

  1. __c()
  2. _methodFunc()

总结

JSPatch充分应用了 runtimeJavaScriptCore 两大技术,实现了OC和JS的通信。

对于OC调用JS,和JS调用OC 都是使用的消息转发的机制;这两种调用是一个相反的过程。

参考