前言
JSPatch是一个可以动态更新iOS APP的开源库。通过JSPatch,可以使用JS书写原生代码,动态更新APP,替换项目原生代码修复Bug。
JSPatch充分应用了Objective-C的runtime,来实现自己的功能。
这篇文章将结合Demo,来分析JSPatch的主要的实现过程。包括:
- 使用JS新建类
- 使用JS替换原有方法
- 使用JS添加全新方法
- OC调用JS定义的方法
- 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
| defineClass('JPViewController', { handleBtn: function(sender) { var tableViewCtrl = JPTableViewController.alloc().init() self.navigationController().pushViewController_animated(tableViewCtrl, YES) } })
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。它做了两件事:
- 重新定义
JPViewController
的handleBtn:
方法;现在点击该button的话,会新建一个tableView
- 定义一个新的类型
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 = {}
var realClsName = declaration.split(':')[0].trim()
_formatDefineMethods(instMethods, newInstMethods, realClsName) _formatDefineMethods(clsMethods, newClsMethods, realClsName)
var ret = _OC_defineClass(declaration, newInstMethods, newClsMethods) 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:
的实现。
先来说一下方法替换的具体流程:
- 创建一个
ORIGhandleBtn:
selector,指向handleBtn:
的IMP
- 创建一个
ORIGforwardInvocation:
selector,指向JPViewController
的 forwardInvocation:
的IMP
- 将
handleBtn:
selector 指向 _objc_msgForward
;也就是说执行handleBtn:
这个方法的时候,不会去查找它的实现,会直接进行消息转发(关于消息转发,可以看我的这篇文章 Objective-C runtime - 消息)
- 在OC的全局变量
_JSOverideMethods
中保存这个方法的实现,也就是保存JS中定义的这个方法
- 将
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) {
return function(){ var args = Array.prototype.slice.call(arguments) 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) { 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 += ":" } } 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
- defineClass
- overrideMethod
- JPForwardInvocation
- callSelector
JSPatch.js
- __c()
- _methodFunc()
总结
JSPatch充分应用了 runtime 和 JavaScriptCore 两大技术,实现了OC和JS的通信。
对于OC调用JS,和JS调用OC 都是使用的消息转发的机制;这两种调用是一个相反的过程。
参考