咱们都知道WebKit是个渲染引擎,简单来讲负责页面的布局,绘制以及层的合成,可是WebKit工程中不单单有关于渲染相关的逻辑,也集成了默认的javascript引擎--JavaScriptCore,目前Safari的js引擎也基于JSC构建,不过有一些私有的优化,整体性能相差不大。JSC的执行理念比较符合传统的引擎逻辑,它包括了2部分:解释器和简单方法JIT
。解释器比较容易理解,针对某种类型的文件解释执行,在JSC中,它的目标文件是由代码构建的语法树生成的字节码文件,相似于java中的字节码,不过在JSC中字节码的执行是在基于寄存器的虚拟机中而不是基于栈,好处在于能够方便的在ARM架构处理器中使用三地址指令,减小了次数较多的出栈和入栈等指令分派以及耗时的内存IO;JIT在java虚拟机中应用比较多,针对执行较屡次的热点方法进行编译为本地方法,执行效率更高,JSC中的JIT同理。
在iOS7中,咱们能够引入JSC框架,这样,咱们能够oc层来操做js层代码的执行。另外JSC暴露了许多C层面的接口,咱们也能够在底层来构建自定义的js执行环境,操做执行js代码,可控执行可扩展性更强。javascript
既然有了这么给力的引擎,咱们在构建hybrid app时能够使用JSC来代替cordova的webViewJavascriptBridge框架完成简易的接口暴露,将来在oc层逐渐能够将UI组件模块化,并经过JSExport暴露接口,由js层负责调用相应模块的初始化方法完成界面的hybrid化。
oc端初始化一个js执行上下文JSContext对象很容易, [[JSContext alloc] init]
便可,可是在hybrid app中,经过这种方式初始化JSContext与承载页面的UIWebVIew并非同一个js环境,所以咱们须要获取UIWebView对应的JSContext。可是apple官方并未提供相关的方法,不过这边难不倒某些人,有些人发现,经过KVC的方式可获取UIWebView对应的JSContext,方式以下[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]
。一旦获取到对应的JSContext,咱们能够作的就有不少了。java
// 获取对应的JSContext JSContext *context=[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; // 设置JSContext的错误处理函数 [context setExceptionHandler:^(JSContext *context, JSValue *value) { NSLog(@"oc catches the exception: %@", value); }]; // 组件化某个功能类或UIController ShowjoyFad *sf=[ShowjoyFad new]; // 暴露改类至JSContext中,在js层的全局属性中咱们能够访问该类,如window.showjoyFad context[@"showjoyFad"]=sf; context[@"ViewController"] = self; // 引用js层定义的函数 JSValue * abc = context[@"abc"]; // 执行 JSValue * ret = [abc callWithArguments:@[@"helloworld"]]; NSLog(@"ret: %@",[ret toString]);
经过简单的例子能够很明显的看出JSC通讯的简洁性,与android的WebView通讯相似,native端能够直接讲接口注入到js上下文中,js在什么时候的时机调用函数。可是这里涉及到一个比较棘手的问题,JSContext的获取实在UIWebView的那个阶段呢?
我作过一个测试:首先在UIWebView的webViewDidStartLoad阶段建立JSContext并暴露oc端的方法,在加载一级页面时js正常调用oc的方法,而跳转到二级页面中却没法执行oc的方法;而在webViewDidStartLoad阶段因为并未加载完js文件, 所以js层定义的函数在oc端没法执行。
其次,在webVIewDidFinishLoad阶段建立JSContext并透出oc方法,因为加载js阶段在webVIewDidFinishLoad阶段以前,所以一级页面js没法调用oc方法,可是二级页面同理也是如此,可是因为js代码是在iOS的UI线程执行,所以为了让js能够调用oc方法,能够经过在js设置setTimeout来让任务放到执行队列的末端,先执行oc层的webVIewDidFinishLoad方法,待任务完成后再执行js中的异步代码,经过这种方式能够完成js调用oc方法;反过来,oc层调用js函数没有任何问题,由于在webVIewDidFinishLoad阶段js代码已执行完毕(除了异步代码)。
为此,能够经过实现一个简易的框架来完成js层和oc层的交互,为了更好的兼容性,只有在webVIewDidFinishLoad阶段建立JSContext。而在js层则有两种方式来监测并执行oc的方法:
1,在oc层的webVIewDidFinishLoad阶段,暴露oc接口以后,经过JSContext或者UIWebView的stringByEvluateJavascriptString方法构建一个webViewDidFinishLoad
事件,js端进行侦听并调用
2,简单的经过setTimeout将js的执行顺序排至队列末端
经过上述方法,构建了一个简单的JSCBridge,可是缺点也很明显,对oc端接口暴露时机有硬性要求,而且js执行oc端的代码始终是异步,有违咱们的初衷。android
在下一节中,介绍利用JSC高效通讯的另外一种hack方法,请期待!
web