前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >深入理解JSCore后续

深入理解JSCore后续

作者头像
拉维
发布2019-08-12 15:53:22
1.6K0
发布2019-08-12 15:53:22
举报
文章被收录于专栏:iOS小生活iOS小生活

前几天写过一篇文章深入理解JSCore,本文是对该文的补充与再理解。

总体来说,JavaScriptCore为原生编程语言Objective-C、Swift提供调用JavaScript程序的动态能力,还能为JavaScript提供原生调用能力以弥补前端能力的不足。正因JavaScriptCore的这种桥梁作用,故而出现了出多使用JavaScriptCore开发APP的框架,比如ReactNative、Weex、小程序、WebView Hybrid等框架。

那么你有没有想过,为什么这些框架都不约而同地使用JavaScriptCore作为前端和iOS原生的桥梁呢?

JavaScriptCore,原本是WebKit中用于解释执行JavaScript代码的核心引擎。解释执行JavaScript代码的引擎自JavaScript诞生之日起就有,不断演进,一直发展到现在,比如苹果的JSCore引擎、谷歌的v8引擎等。在iOS7之前,苹果并没有开放JavaScriptCore引擎,如果你想使用JSCore的话,就需要手动从开源WebKit中编译出来,而且其接口都是C语言,这对于iOS开发者而言非常不友好。

在iOS7之后,苹果公司将JavaScriptCore框架引入iOS系统,并将其作为系统级的框架开放给开发者使用。此时,接口已经是Objective-C进行封装的了,这对iOS开发者来说就十分友好了。

JavaScriptCore框架的框架名是JavaScriptCore.framework。由于苹果公司已经内置了JavaScriptCore框架,而且性能不逊色于谷歌的V8等其他引擎,所以前端开发APP框架就都不约而同将JavaScriptCore框架作为自己和iOS原生的桥梁。

JavaScriptCore框架

JavaScriptCore框架主要由JSVirtualMachine、JSContext和JSValue类组成。

JSVirtualMachine的作用,是为JavaScript代码的运行提供一个虚拟机环境。同一个时间内,JSVirtualMachine只能执行一个线程,如果想要多个线程执行任务,那么你可以创建多个JSVirtualMachine。每个JSVirtualMachine都有自己的GarbageCollection(垃圾回收机制),所以多个JSVirtualMachine之间的对象无法传递。

JSContext是JavaScript运行环境的上下文,负责原生和JS之间的数据传递。

JSValue是JavaScript的值对象,用来记录JavaScript的原始值,并提供进行原生值对象转换的接口方法。

JSVirtualMachine、JSContext、JSValue之间的关系,如下图:

可以看出,JSVirtualMachine里包含了多个JSContext,同一个JSContext里又可以有多个JSValue。

JSVirtualMachine、JSContext、JSValue类提供的接口,能够让原生应用执行Javascript代码,访问JavaScript变量,访问和执行JavaScript函数;也能够让JavaScript执行原生代码,使用原生的类。

那么,解释执行JavaScript代码的JavaScriptCore和原生应用又是怎么交互的呢?

我们先来看看下面这张图:

可以看到,JavaScriptCore中的每个JSVirtualMachine都对应这一个原生线程,同一个JSVirtualMachine中可以使用JSValue与原生线程通信,遵循的是JSExport协议:原生线程可以将类方法和属性提供给JavaScriptCore来使用,JavaScriptCore可以将JSValue提供给原生线程使用

如果你想在原生代码中使用 JavaScript 中的函数对象,可以通过 callWithArguments方法传入参数,然后实现它的调用,实现代码如下:

代码语言:javascript
复制
// 解析执行 JavaScript 脚本
[context evaluateScript:@"function addition(x, y) { return x + y}"];
// 获得 addition 函数
JSValue *addition = context[@"addition"];
// 传入参数执行 addition 函数
JSValue *resultValue = [addition callWithArguments:@[@(4), @(8)]];
// 将 addition 函数执行的结果转成原生 NSNumber 来使用。
NSLog(@"function is %@; reslutValue is %@",addition, [resultValue toNumber]);

如上面代码所示:首先,JSContext通过evaluateScript方法获取JavaScript代码中的addition函数,并保存为一个JSValue对象;然后通过JSValue的callWithArguments方法传入addition函数所需参数x、y以执行函数。

如果要在原生代码中调用JavaScript全局函数,你需要使用JSValue的invokeMethod:withArguments方法。比如,Weex框架就是使用这个方法来获取JavaScript函数的。

相关代码路径是 incubator-weex/ios/sdk/WeexSDK/Sources/Bridge/WXJSCoreBridge.mm ,核心代码如下:

代码语言:javascript
复制
- (JSValue *)callJSMethod:(NSString *)method args:(NSArray *)args {
    WXLogDebug(@"Calling JS... method:%@, args:%@", method, args);
    return [[_jsContext globalObject] invokeMethod:method withArguments:args];
}

可以看到,JSContext里有一个globalObject属性,该属性一个全局对象,是JSValue类型,里面记录了JSContext中的所有变量与函数。因此,可以通过globalObject来执行invokeMethod:withArguments:方法,进而调用JavaScript中的全局函数

通过上面的分析我们知道,通过JSContext的evaluateScript:方法,我们可以在原生代码中执行JavaScript脚本,并且使用JavaScript中的值对象和函数对象。那么,JavaScript又是如何调用原生代码的呢

我先给出一段示例代码:

代码语言:javascript
复制
// 在 JSContext 中使用原生 Block 设置一个减法 subtraction 函数
context[@"subtraction"] = ^(int x, int y) {
    return x - y;
};
// 在同一个 JSContext 里用 JavaScript 代码来调用原生 subtraction 函数
JSValue *subValue = [context evaluateScript:@"subtraction(4,8);"];
NSLog(@"substraction(4,8) is %@",[subValue toNumber]);

可以看出,JavaScript调用原生代码的方式就是:

  • 首先,在JSContext中使用原生Block设置一个减法函数subtraction;
  • 然后,在同一个JSContext里使用JavaScript代码来调用原生subtraction函数。

除了Block外,我们还可以通过JSExport协议来实现在JavaScript中调用原生代码,也就是说,让遵循JSExport协议的类,能供JavaScript使用。Weex框架里就有个遵循了JSExport协议的WXPolyfillSet类,使得JavaScript也能够使用原生代码中的NSMutableSet类型。

WXPolyfillSet 的头文件代码路径是 incubator-weex/ios/sdk/WeexSDK/Sources/Bridge/WXPolyfillSet.h ,内容如下:

代码语言:javascript
复制
@protocol WXPolyfillSetJSExports <JSExport>
// JavaScript 可以使用的方法
+ (instancetype)create;
- (BOOL)has:(id)value;
- (NSUInteger)size;
- (void)add:(id)value;
- (BOOL)delete:(id)value;
- (void)clear;
@end
// WXPolyfillSet 遵循 JSExport 协议
@interface WXPolyfillSet : NSObject <WXPolyfillSetJSExports>
@end

可以看到, WXPolyfillSet 通过 WXPolyfillSetJSExports 协议,提供了一系列方法给JavaScript使用。

现在我们已经理解了原生和JavaScript的互通方式,知道了它们的互通依赖于虚拟机环境JSVirtualMachine。接下来,我们需要对JSCore引擎做更为深入的了解,这样才能更好地用好这个框架。

JSCore是WebKit中默认内嵌的JS引擎,很多基于WebKit分支开发的浏览器都开发了自己的JS引擎,比如Chrome的V8,这些JS引擎的使命都一样,那就是解释执行JS代码

JSCore 包含解释器和运行时两部分,解释器主要将高级的JS脚本语言编译成字节码,运行时主要用来管理运行时的内存空间

JavaScriptCore解释执行JavaScript代码分为两步:

第一步,对JavaScript进行词法分析、语法分析,生成ByteCode字节码。这一步其实就是编译,但是编译JS脚本语言跟编译其他的编译型语言不同的是,编译完脚本语言之后,并不会生成存在磁盘中的可执行文件,而是直接解释执行

第二步,由interpreter来解释执行,解释执行的过程先由LLInt(Low Level Interpreter)来执行上一步生成的字节码,JSCore会对运行频次高的函数或者循环进行优化(优化器是JIT)

小结

JavaScriptCore就是解释执行JS的引擎,它还提供了原生与前端相互调用的接口。通过JSContext的evaluateScript方法,原生可以调用JavaScript代码;通过遵循JSExport协议,JavaScript内部可以使用原生的方法和属性。

前端的又是在于能够快读编写UI,原生的优势在于对平台特性的天然支持。现在我们有了能够打通二者的武器,就能够利用好二者的优势,扬长避短,做一些有意思的事儿。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-06-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 iOS小生活 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云开发 CloudBase
云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档