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实现的原理解析系列

JavaScriptCore-b的更多相关文章

  1. How Javascript works (Javascript工作原理) (二) 引擎,运行时,如何在 V8 引擎中书写最优代码的 5 条小技巧

    个人总结: 一个Javascript引擎由一个标准解释程序,或者即时编译器来实现. 解释器(Interpreter): 解释一行,执行一行. 编译器(Compiler): 全部编译成机器码,统一执行. ...

  2. iOS引入JavaScriptCore引擎框架(一)

    JavaScriptCore引擎     我们都知道WebKit是个渲染引擎,简单来说负责页面的布局,绘制以及层的合成,但是WebKit工程中不仅仅有关于渲染相关的逻辑,也集成了默认的javascri ...

  3. 判断js引擎是javascriptCore或者v8

    来由   纯粹的无聊,一直在搜索JavaScriptCore和SpiderMonkey的一些信息,却无意中学习了如何在ios的UIWebView中判断其js解析引擎的方法: if (window.de ...

  4. DDD 领域驱动设计-看我如何应对业务需求变化,愚蠢的应对?

    写在前面 阅读目录: 具体业务场景 业务需求变化 "愚蠢"的应对 消息列表实现 消息详情页实现 消息发送.回复.销毁等实现 回到原点的一些思考 业务需求变化,领域模型变化了吗? 对 ...

  5. CSS3 3D立方体效果-transform也不过如此

    CSS3系列已经学习了一段时间了,第一篇文章写了一些css3的奇技淫巧,原文戳这里,还获得了较多网友的支持,在此谢过各位,你们的支持是我写文章最大的动力^_^. 那么这一篇文章呢,主要是通过一个3D立 ...

  6. DDD 领域驱动设计-谈谈 Repository、IUnitOfWork 和 IDbContext 的实践(3)

    上一篇:<DDD 领域驱动设计-谈谈 Repository.IUnitOfWork 和 IDbContext 的实践(2)> 这篇文章主要是对 DDD.Sample 框架增加 Transa ...

  7. DDD 领域驱动设计-两个实体的碰撞火花

    上一篇:<DDD 领域驱动设计-领域模型中的用户设计?> 开源地址:https://github.com/yuezhongxin/CNBlogs.Apply.Sample(代码已更新) 在 ...

  8. node中的Stream-Readable和Writeable解读

    在node中,只要涉及到文件IO的场景一般都会涉及到一个类-Stream.Stream是对IO设备的抽象表示,其在JAVA中也有涉及,主要体现在四个类-InputStream.Reader.Outpu ...

  9. C#~异步编程再续~await与async引起的w3wp.exe崩溃-问题友好的解决

    返回目录 关于死锁的原因 理解该死锁的原因在于理解await 处理contexts的方式,默认的,当一个未完成的Task 被await的时候,当前的上下文将在该Task完成的时候重新获得并继续执行剩余 ...

随机推荐

  1. 几个shell自动化脚本(定期清理、磁盘空间、搜寻关键字)

    发布时间:2014-10-28 09:52:17   编辑:AHLinux.com 分享几个精致耐用的shell脚本,分别用于定期清理暂存文件.检查硬盘空间使用率.搜寻所有记录文件的关键字符串.有需要 ...

  2. 屌丝程序猿赚钱之道 之APP

    假设你已经通过APP赚到了钱,那么本文对你而言没有意义.倒是希望你可以给我们诸多建议. 通过制作APP或者说手机应用赚钱,相信是非常多程序猿希望做的事情.也确实有一些人通过APP赚到了钱. 对于程序猿 ...

  3. Android API 文档 离线秒开方法

    http://blog.csdn.net/haifengzhilian/article/details/39898627 也是最近才看Android开发,但是,它的API文档无论是在线还是离线的,实在 ...

  4. Java中List和ArrayList的区别(转)

    List是一个接口,而ListArray是一个类.  ListArray继承并实现了List.  所以List不能被构造,但可以向上面那样为List创建一个引用,而ListArray就可以被构造.  ...

  5. 8086FLAG寄存器

    8086中的FLAG寄存器也就是状态标志位寄存器.它用来存储一些指令的计算结果,比如加法减法中的进位:为CPU运行某些命令提供根据,比如DF它决定是往前走指针还是向后走指针:总之状态寄存器存放的被称为 ...

  6. jsp-javabean-setproperty介绍

    李兴华<java web开发实战经典>第7章关于javabean的讲解中说道:<jsp:setProperty>标签一共有4种使用方法·下面列出了4种操作语法的格式:   设置 ...

  7. mysql导出部分数据的几种方法(摘录)

    mysql虽然可以使用mysqldump来进行数据的到处,可是在很多场合的需求都不一样,比如我只要导出某个字段呢?只要导出某些我需要的数据呢? 这个时候mysqldump可能就不大好使了 方法一. i ...

  8. Android开发之意图解析

     android中意图(intent)就是告诉系统要做某件事情.比如要拨打电话或者发送短信. 或者在一个Activity中点击按钮跳转到另外一个activity时也用到意图.意图分为两种:显示意图和隐 ...

  9. nginx同时监听本机ipv4/ipv6端口

    修改nginx.conf配置文件 server { listen ; listen [::]:; } 0.0.0.0  表示本机所有ipv4地址,需要监听特定地址替换即可 [::]  表示本机所有ip ...

  10. python 入门1

    python的历史 Python是一种解释型.面向对象.动态数据类型的高级程序设计语言. Python由Guido van Rossum于1989年底发明,第一个公开发行版发行于1991年. 像Per ...