美文网首页iOS开发
iOS中JavaScriptCore的简单介绍和使用

iOS中JavaScriptCore的简单介绍和使用

作者: Jay_小咖 | 来源:发表于2018-12-11 23:35 被阅读65次

    移动终端的应用按照技术的实现可分为三大类:本地化应用(Native App),基于web的Web App,和混合型应用(Hybrid App)。由于HTML5的出现,它的跨平台和相对于原生应用的廉价等优势,使得现在越来越多的个人开发者或公司也青睐于使用H5来构建自己的移动应用。接下来介绍的是介于Native App和Web App之间的Hybrid App,在iOS应用开发中,可使用UIWebView(WKWebView)来加载一个网页,在iOS设备上渲染HTML网页和运行JavaScript代码,而且在iOS7之后引入的JavaScriptCore,提供了原生应用与Web页面的功能更强大,使用更简单的通信,只要很少的代码即可实现原生代码与JavaScript的相互调用。

    三类App的对比

    首先,还是要对比下三种App的优劣,这里只是用表格来简单说明一下:

    Web App(网页应用) Hybrid App(混合应用) Native App(原生应)
    跨平台
    开发成本
    体验
    Store获取market认可 不认可 认可 认可
    安装 不需要 需要安装 需要安装
    维护更新 简单 简单 复杂

    由表中可看出各有优劣,开发者应该根据自身的情况来选择适合自己的方式来构建应用。

    JavaScriptCore的介绍

    在iOS7之前,我们只能通过向UIWebView发送stringByEvaluatingJavaScriptFromString:消息来执行一段JavaScript脚本。而如果想在JavaScript调用Objective-C代码,必须打开一个自定义的URL(例如:foo://),然后在UIWebView的delegate方法webView:shouldStartLoadWithRequest:navigationType中进行处理。在OS X Mavericks和iOS 7引入了JavaScriptCore库,它把WebKit的JavaScript引擎用Objective-C封装,提供了简单、快速以及安全的方式接入世界上最流行的语言-JavaScript。JavaScriptCore是封装了JavaScriptCore与Objective-C桥接的API,可以轻松实现JS与OC的互调。

    利用JavaScriptCore,可以实现的功能:

    • 在OC和JS之间可以无缝的传递值或对象;
    • 编写JS调用OC的代码时,可以使用现代的OC语法,如Blocks、下标等;
    • 创建混合对象,如原生对象可以将JS的一个属性或函数作为一个属性,如JS的json,OC可通过toDictionary创建一个原生字典对象;

    利用OC与JS结合开发的好处:

    • 实现快速开发
      如App里面的某个模块的业务需求变化得非常频繁,或者在某个功能复复杂的模块中想新增一个复杂的功能模块,那么我们可以考虑使用H5和JavaScript进行开发,这样比使用Objective-C开发效率更高,后期的维护更方便。
    • 团队职责划分明确
      可以明确的划分工作职责,H5和JavaScript开发能力较强的,专门负责开发web页面,原生App开发的开发人员专门负责原生App的开发,两者开发后,后期进行联调工作。
    • JS的解释型特性
      由于JavaScript是一种解释型的语言,修改了js代码后,可以马上看到效果。
    • 跨平台(编写一次,多平台运行)
      具有跨平台的好处,JavaScript只需编写一次,可运行与多个平台(如iOS和Android)。

    JavaScriptCore的使用

    首先,开始使用JavaScriptCore框架,在Xcode中导入framework,然后在相关类中引入以下头文件:
    JavaScriptCore/JavaScriptCore.h
    该头文件中包含了以下的常用的头文件:

    JSContext.h
    JSValue.h
    JSManagedValue.h
    JSVirtualMachine.h
    JSExport.h

    JSContext:是运行JavaScript代码的环境,需要用它来执行JavaScript代码,一个JSContext就是一个全局环境的变量,相当于window。我们可以很容易的利用JSContext来创建JS变量、函数等。
    JSValue:表示对应于JavaScript中原始的很多值,如boolean,array,string等,通过一系列方法去访问到其可能的值,以保证有正确的Foundation值。如JS的array,通过JSValue的toArray方法就可以转换为Objective-C的NSArray类型,JS的json,通过JSValue的toDictionary方法,可转换为OC中的NSDictionary字典类型的数据等等,其他的类型都是类似的;
    JSExport协议:实现这个协议,可以将我们定义的方法、属性等都自动地提供给JavaScript代码,可以在JS代码中直接使用我们自定义的原始代码;
    JSVirtualMachin:字面上可以看出,它就像是虚拟机一样,拥有自己的堆结构和垃圾回收机制,大多数情况下不会和它交互,除非一些涉及内存管理和多线程的处理的情况,为JavaScript的允许提供底层资源。
    JSManagedValue
    OC和swift的对象都是使用引用计数进行管理,而JavaScript的是垃圾回收机制。为了避免两种语言交互使用时产生循环引用的问题,需要使用JSManagedValue来进行内存辅助管理。

    JScontext / JSValue

    可通过以下方式来创建一个全局的JSContext环境,并利用它来执行js代码:

    /// 创建一个JSContext实例,只需创建一次
    ///JSContext *context = [[JSContext alloc] init];//有问题,内置的函数都不会执行
    /// 每个UIWebView都有自己的JSContext实例,使用KVC获取JSContext实例
    self.context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    [context evaluateScript:@"var num1 = 5 + 5;"];
    [context evaluateScript:@"var arr = ['a','b','c']"];
    [context evaluateScript:@"var triple = function(value){return value * 5}"];//定义一个函数
    JSValue *returnValue = [context evaluateScript:@"triple(num1);"];//返回一个JSValue值
    
    

    任何出自 JSContext 的值都被包裹在一个 JSValue 对象中。像 JavaScript 这样的动态语言需要一个动态类型,所以 JSValue 包装了每一个可能的 JavaScript 值:字符串和数字;数组、对象和方法;甚至错误和特殊的 JavaScript 值诸如 null 和 undefined。

    异常捕获

    当我们调用JS时,可能会出现错误,这时我们需要追踪和记录出现的语法、类型和运行时的错误,通过设置context上下文的exceptionHandler属性来完成,如下所示,exceptionHandler是一个接受JSContext本身的引用及exception异常信息的回调处理。

    /// 处理异常
    self.context.exceptionHandler = ^(JSContext *context,JSValue *exception){
        NSLog(@"exception:%@",exception);
        /// 通过js alert,提示错误信息
        NSString *errorFunc = [NSString stringWithFormat:@"alert(\"%@\");",[exception toString]];
        [weakSelf.context evaluateScript:errorFunc];
    };
    

    JavaScript调用OC

    上面我们是通过JSContext实例来在原生代码中调用JavaScript,现在我们来看如何实现在JavaScript中调用原生代码。实现的方式有两种:BlocksJSExport协议

    Blocks

    context里的一个标识符,如getCurSystemVersionOnly,就是对应JavaScript的一个函数名,将OC中的block赋值给context中的一个标识符,JavaScriptCore就自动把block封装在JavaScript的函数里,也就转换成了js的方法,当在JavaScript中调用时,直接调用对应的原生方法。

    self.context[@"getCurSystemVersionOnly"] = ^(NSString *callBack){
       NSString *curVerison = [[UIDevice currentDevice] systemVersion];
       /// 获取传递的参数,也可以直接在block中获取
       NSArray *args = [JSContext currentArguments];
       JSContext *currentContext = [JSContext currentContext];//注意不要用self.context,防止强引用循环
        if (args != nil && [args count] != 0) {
           /// 获取回调函数,需要截取函数名
           /*
           function callBack(version){
             alert(version);
           }
           */
        weakSelf.getVersionCallBack = [weakSelf truncateFuncName:[[args objectAtIndex:0] toString]];
         /// 执行js中的回调函数来传值
         if (weakSelf.getVersionCallBack) {
         [[[JSContext currentContext] globalObject] invokeMethod:weakSelf.getVersionCallBack withArguments:[NSArray arrayWithObjects:curVerison, nil]];
            }
          }
      };
    /// 另声明一个函数,给js调用,以便js可以自定义更多内容后再调用这个接口
    [self.context evaluateScript:@"globalObj.getCurSystemVersion = function(callBack){getCurSystemVersionOnly(callBack);}"];
    除了使用context[@""]=block的方式,也可使用以下的方式:
    void (^block)() = ^(NSString *string) {
       NSLog(@"%@", string);
    };
    /// 通过setObject:forKeyedSubscript的方法
    [self.context setObject:block forKeyedSubscript:@"print"];
    

    JSExport协议

    JSExport是一个协议,它可以让原生的属性、方法直接称为JavaScript的属性或者方法,将原生的对象全部导出当成JavaScript的对象使用。

    首先定义一个继承自JSExport的协议(不可直接使用它),然后在JavaScript调用的类中实现这个协议。

    ///1. 定义一个继承自JSExport的协议
    @protocol MJwebViewJSExport <JSExport>
    
    @property (nonatomic,strong) NSString *name;
    @property (nonatomic,strong) NSString *password;
    
    - (void)print:(NSString *)message;
    
    @end
    

    然后在使用JavaScript的类中实现:

    ///2. 让类遵守协议
    @interface MJwebBaseViewController () <UIWebViewDelegate,MJwebViewJSExport>
    ///4. 将本类的句柄复制给context,JS调用时就可使用这个句柄进行调用
    self.context[@"JYObject"] = self;
    ///5. 直接js调用 
    [self.context evaluateScript:@"JYObject.print('hello guy!')"];
    
    #pragma mark - MJwebViewJSExport
    ///3. 实现协议方法
    - (void)print:(NSString *)message {
        NSLog(@"message:%@",message);
        
    }
    

    JavaScript中的调用:

    function useJSExport(value){
    //直接调用
    JYObject.print("我是测试的值!");
    }
    

    小结

    以上只是简单的介绍下JavaScriptCore的基本使用,没有涉及到太多的内存管理的知识,JSManagedValue的使用等都没有具体介绍。iOS开发者除了要知道JavaScriptCore的使用外,对JavaScript的相关知识也要有一定的了解,这样才能更好的完成js接口的开发,使更加符合需求。

    参考资料

    JavaScriptCore初探

    JavaScriptCore-NSHipster

    官方JavaScriptCore文档

    相关文章

      网友评论

        本文标题:iOS中JavaScriptCore的简单介绍和使用

        本文链接:https://www.haomeiwen.com/subject/fxhshqtx.html