• JavaScriptCore-b


    JavaScriptCore提供了JavaScript和Objective-C桥接的Obj-C API。JavaScriptCore提供了让我们脱离UIWebView执行JavaScript脚本的能力,以及使用现代的Objective-C语法(例如Blocks和下标)在Objective-C和JavaScript之间无缝的传递值或者对象。借助JavaScriptCore,我们只需要很少的代码就可以完成OC与JS的交互通信,下面让我们一睹它的风采。同样,这篇文章会用JavaScriptCore有关API重写上一篇文章

    一、JavaScriptCore概述

    使用JavaScriptCore需要导入头文件"#import <JavaScriptCore/JavaScriptCore.h>",进入头文件我们会看到里面的类不多,只有下面5个:

    1
    2
    3
    4
    5
    #import "JSContext.h"
    #import "JSValue.h"
    #import "JSManagedValue.h"
    #import "JSVirtualMachine.h"
    #import "JSExport.h"

    JSContext: 代表JavaScript的运行环境,创建JSContext后,可以来执行JavaScript代码。

    JSValue: 代表JavaScript实体,一个JSValue可以是JavaScript中的任意类型:字符串和数字;数组、对象和方法;甚至错误和特殊的 JavaScript 值诸如 null 和 undefined。任何JSContext的值都被包裹在一个JSValue对象中。

    JSManagedValue: 本质上是一个JSValue,用来处理内存管理中的一些特殊情形,它能帮助OC引用技术和JS垃圾回收这两种内存管理机制之间进行正确的转换。

    JSExport: 这是一个协议,可以用这个协议来将原生对象导出给JavaScript,这样原生对象的属性或方法就成为了JavaScript的属性或方法。

    JSVirtualMachine: 代表一个对象空间,拥有自己的堆结构和垃圾回收机制,是运行JS代码的基础。大部分情况下不需要和它直接交互,除非要处理一些特殊的多线程或者内存管理问题。

    二、JavaScriptCore深入

    1. 方法调用

    a. OC调用JS

    1
    2
    3
    4
    5
    6
    7
    8
    //使用UIWebView执行js脚本的方法
    - (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
     
    //使用JSContext执行js脚本的方法
    - (JSValue *)evaluateScript:(NSString *)script;
     
    //使用JSValuet执行js脚本的方法
    - (JSValue *)callWithArguments:(NSArray *)arguments;

    b. JS调用OC

    有两种方式:block和JSExport协议

    通过block可以直接讲某个功能的函数,注入给JSContext,使其调用,但要注意内存泄露

    通过继承JSExport协议,可以将OC的方法,属性注入给JSContext,然后调用

    2. 错误处理

    当JavaScript运行时出现异常,会回调JSContext的exceptionHandler中设置的Block,然后在OC端进行错误处理

    1
    2
    3
    context.exceptionHandler = ^(JSContext *context, JSValue *exception) {
          NSLog(@"JS Error: %@", exception);
    };

    3. 内存管理 

    Objective-C的内存管理机制是引用计数,JavaScript的内存管理机制是垃圾回收。在大部分情况下,JavaScriptCore能做到在这两种内存管理机制之间无缝无错转换,但也有少数情况需要使用到JSManagedValue对象解决,后面会给出对应链接。

    三、使用JavaScriptCore重写

     沿用之前的示例,其他地方均无改动,只修改了两边交互的相关代码:

    OC端:

    1. 初始化JScontext

    1
    self.jsContext = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

    2. 注入JS代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    __block typeof(self) weakSelf = self;
    //JS调用OC方法列表
    self.jsContext[@"showMobile"] = ^ {
        dispatch_async(dispatch_get_main_queue(), ^{
            [weakSelf showMsg:@"我是下面的小红 手机号是:18870707070"];
        });
    };
     
    self.jsContext[@"showName"] = ^ (NSString *name) {
        dispatch_async(dispatch_get_main_queue(), ^{
            NSString *info = [NSString stringWithFormat:@"你好 %@, 很高兴见到你",name];
            [weakSelf showMsg:info];
        });
    };
     
    void (^_showSendMsg) (NSString *num, NSString *msg) = ^ (NSString *num, NSString *msg) {
        dispatch_async(dispatch_get_main_queue(), ^{
            NSString *info = [NSString stringWithFormat:@"这是我的手机号: %@, %@ !!",num,msg];
            [self showMsg:info];
        });
    };
     
    [self.jsContext setObject:_showSendMsg forKeyedSubscript:@"showSendMsg"];

    3. 执行JS端代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    //OC调用JS方法列表
    - (IBAction)btnClick:(UIButton *)sender {
        if (sender.tag == 123) {
            //使用jsContext
            [self.jsContext evaluateScript:@"alertMobile()"];
        }
         
        if (sender.tag == 234) {
            //使用webView
            [self.webView stringByEvaluatingJavaScriptFromString:@"alertName('小红')"];
        }
         
        if (sender.tag == 345) {
            //使用jsValue
            JSValue *jsValue = [self.jsContext objectForKeyedSubscript:@"alertSendMsg"];
            [jsValue callWithArguments:@[@"18870707070",@"周末爬山真是件愉快的事情"]];
        }
    }

    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
    //提供给OC调用JS的方法列表
    function alertMobile() {
        alert('我是上面的小黄 手机号是:13300001111')
    }
     
    function alertName(msg) {
        alert('你好 ' + msg + ', 我也很高兴见到你')
    }
     
    function alertSendMsg(num,msg) {
        alert('这是我的手机号:' + num + ',' + msg + '!!')
    }
     
    //JS响应方法列表
    function btnClick1() {
        showMobile()
    }
     
    function btnClick2() {
        showName('xiaohuang')
    }
     
    function btnClick3() {
        showSendMsg('13300001111''Go Climbing This Weekend !!!')
    }

    了解过JavaScriptCore的原理及核心文件,核心类的作用,再过来上手重写,已经没有什么什么阻碍了,但是仍然有需要注意的地方。因为JavaScript是单线程的,在JS调用OC的代码都是在线程中执行的,所以和界面相关操作我们需要切换到主线程来刷新,其他流程和之前保持一致,学习这一部分参考了很多其他资料,文章后面给出了有关JavaScriptCore的介绍和实现原理解析的文章链接,有兴趣的同学可以传送之深入学习

    四、后记

    苹果的技术每年都会更新,在JavaScript这一块,每年也会出现新的惊喜,iOS8发布的时候,苹果又推出了WKWebView,对之前的UIWebView进行了一次脱胎换骨的重构(将UIWebView和UIWebViewDelegate重构成了14个类和3个协议),功能也更加完善和强大,稳定性和性能也明显提高。之前看到过一篇文章,详细介绍了WKWebView的相关API,对我了解这一模块提供了很大的帮助,后面我也系统的看完了整个WKWebView的API,受益匪浅,看的时候,我没有直接过去看这篇文章,而是先自己通读API然后对比这篇文章,查看理解方面的出入,同时也加深了印象,同学们也可以借鉴这种方式。下一篇文章,我们使用WKWebView的相关API来完成这个示例

    戳这里:本文的DEMO地址欢迎star

    参考资料(戳这里):

    >  http://nshipster.cn/javascriptcore/

    >  https://hjgitbook.gitbooks.io/ios/content/04-technical-research/04-javascriptcore-note.html

    >  JavaScriptCore实现的原理解析系列

  • 相关阅读:
    2021“MINIEYE杯”中国大学生算法设计超级联赛(4)
    Spring Boot从入门到精通(十一)集成Swagger框架,实现自动生成接口文档
    Spring Cloud 从入门到精通(二)集成 Nacos 构建微服务实现服务注册
    Spring Cloud 从入门到精通(一)Nacos 服务中心初探
    Apache HBase 1.7.1 发布,分布式数据库
    DB2 SQL Error: SQLCODE=-668, SQLSTATE=57016错误解决方法
    脱离OBDeploy工具,手工部署OceanBase方法
    剑指Offer26.树的子结构
    剑指Offer21.调整数组顺序使奇数偶数前面
    剑指Offer14-I|LeetCode343.剪绳子|整数拆分
  • 原文地址:https://www.cnblogs.com/isItOk/p/5648254.html
Copyright © 2020-2023  润新知