iOS引入JavaScriptCore引擎框架

JavaScriptCore引擎

    我们都晓得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代码,可控实行可扩展性更强。

hybrid运用构建

    既然有了这么给力的引擎,我们在构建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,我们能够做的就有很多了。

// 猎取对应的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端的代码始终是异步,有违我们的初志。

为什么摒弃第一种计划

UIWebView的JSContext猎取

    上篇中,我们经由历程简朴的kvc猎取UIWebVIew的JSContext,然则实际上,apple并未给开辟者供应接见UIWebView的要领,虽然经由历程KVC可到达目的,然则当APP采纳该种hack要领时,有很大概率不能经由历程APP Store的考核,这关于一个基于上线的贸易APP而言是难以忍受的,所以我们必需寻觅另一种要领来猎取UIWebView的JSContext而且充足平安易用,因而我们需转移眼光。

处置惩罚

WebFrameLoadDelegate

    在OS X中,WebFrameLoadDelegate担任WebKit与NSWebView的通讯,由于NSWebView内部依然运用WebKit衬着引擎,若要侦听衬着历程当中的一系列事宜,则必需运用WebFrameLoadDelegate对象:
        1、加载历程:

          在一个接见一个网页的的全部历程,包含最先加载,加载题目,加载完毕等。webkit都邑发送响应的音讯给WebFrameLoadDelegate 。

              webView:didStartProvisionalLoadForFrame:最先加载,在这里猎取加载的url
              webView:didReceiveTitle:forFrame:猎取到网页题目
              webView:didFinishLoadForFrame:页面加载完成

        2、毛病的处置惩罚:

          加载的历程当中,有能够会发作毛病。毛病的音讯也会发送给WebFrameLoadDelegate 。我们能够在这两个函数内里对毛病信息举行处置惩罚

             webView:didFailProvisionalLoadWithError:forFrame: 这个毛病发作在要求数据之前,最常见是发作在无效的URL或许收集断开没法发送要求
             webView:didFailLoadWithError:forFrame: 这个毛病发作在要求数据以后

    但是在iOS中呢?我尝试过,并没有WebFrameLoadDelegate这个对象,看来iOS中的WebKit框架并未供应UIWebView这么多的接口,然则有些人经由历程WebKit的源码照样发明了一二,他就是Nick Hodapp。

Nick的发明

    在iOS中,只管没有暴露WebFrameLoadDelegate,然则在详细完成上仍会推断WebKit的implement有无完成这个协定的某些要领,假如完成则仍会实行,而且在webit的WebFrameLoaderClient.mm文件中,

if (implementations->didCreateJavaScriptContextForFrameFunc) {
    CallFrameLoadDelegate(implementations->didCreateJavaScriptContextForFrameFunc, webView, @selector(webView:didCreateJavaScriptContext:forFrame:),
        script.javaScriptContext(), m_webFrame.get());
}

会推断当前的对象有无完成webView:didCreateJavaScriptContext:forFrame:要领,有则实行。该要领会通报三个参数,第一个是与webkit通讯的WebView(此WebView并非UIWebVIew,Nick层做过测试经由历程猎取的WebView并不能遍历到我们须要的UIWebVIew,因而推想,这个WebView是一个UIView的proxy对象,不是UIView类);第二个则是我们想要猎取的JSContext;第三个参数是webkit框架中的WebFrame对象,与我们的希冀无关。

    为了让webkit实行这个函数,我们必需让对象完成这个要领。由于一切的OC对象都继续自NSObject对象,因而我们能够在NSObject对象上完成该要领,如许能够保证该段代码能够在webkit框架中实行。

    其次,我们既然猎取到了JSContext,然则并不晓得JSContext与UIWebVIew的对应关联,我们的ViewController中能够会有多个UIWebView,如何将猎取的JSContext与UIWebview对应起来也是一个困难。在此处有一个简朴的要领,就是猎取一切的UIWebView对象,在每一个对象中实行一段js代码,在js高低文设置一个变量做为标记,然后在我们猎取的JSContext中推断该变量是不是与遍历的UIWebVIew对象中的对象是不是相称来猎取。如许,我们能够在UIWebView的webViewDidStartLoad和webViewDidFinishLoad之间猎取到JSContext,举行oc和js的双向通讯。

圆满

    我们经由历程上节的论述,大抵邃晓了Nick的思绪,因而能够经由历程协定和类别来完成这类通讯机制,固然采纳oc运行时也是能够的。终究oc端接口的代码放在webView:didCreateJavaScriptContext:forFrame:中,如许js文件只需加载终了便可实行oc的接口要领;而oc端要接见js的接口则可在webVIewDidFinishLoad中实行,圆满处置惩罚接口接见机遇的题目。
    在js端,由于只需暴露在全局的函数声明才够让oc端接见,这就限定了js端的灵活性。我尝试过在js端经由历程“赋值”完成接口的暴露(window.say = function(){alert(“hello world!”)};),在oc端没法接见,只需经由历程一般的函数声明才处置惩罚题目,这能够与JSContext的内存指针援用相干,为了处置惩罚此题目,我经由历程建立一个全局函数来暴露js端的接口对象,经由历程猎取的对象来接见详细的接口要领。

 if(isiOS4JSC){
    // 将注册的要领显露出到window.jscObj的属性上
    var ev = eval;
    $.JSBridge._JSMethod = method;

    // 暴露函数至全局
    // jsc只能实行全局函数声明体式格局定义的函数,不能够将函数指针复制给其他变量实行
    ev('function toObjectCExec() {' +
      'window.jscObj = window.jscObj ? window.jscObj : {};'+
      'window.jscObj["' + methodName + '"] = function (message) {' +
      '  var ret = $.JSBridge._JSMethod(message);' +
      '  return JSON.stringify(ret);' +
      '};' +
      'return jscObj;' +
    '}');

  }

云云,js端的接口暴露就很轻易了。

尾声

    我如今依然置信,如今的iOS hybridAPP的主流通讯体式格局依然适corava的javascriptWebViewBridge,然则跟着jsc引入到iOS7中,本文引见的运用jsc(嵌入js引擎的体式格局)来完成oc和js的通讯将更加盛行,只管如今apple供应的针对jsc的开辟接口文档几乎没有,然则我们经由历程webkit的源码做一些hack的体式格局也不是不能够,毕竟只需UIWebView依然运用webkit举行衬着,这类体式格局会一向有用,除非apple在代码层面针对hack做过滤,不过这类能够性真的很小。我们有理由憧憬将来在iOS和android下更轻易的集成js引擎来完成发起的双向通讯。

    原文作者:RoyalRover
    原文地址: https://segmentfault.com/a/1190000004285316
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞