JavaScript和Native的交互 第二弹

上次简单的写了一下js和Native交互的问题,这篇文章再稍微深入的研究一下JSBinding。作为上文的补充。

JSBinding

JSBinding 是什么?

JSBinding 不是Hybrid很多人都是通过cocos2dx来了解JSbinding这个技术的。

JSBingding 是JSC和Native 之间的桥接。

JS实际上就是JS和Native之间的一个桥梁,通过JSBinding打通了JS代码和Native代码,并在其间实现二者的交流。

JSBinding和Hybrid的区别

Hybrid实际上就是通过webview。js代码实际上还是跑在web上。是web技术和Native之间的桥梁。

二JSBinding并不依赖于webview

Native (原生静态语言): 高性能、更底层、更强大、平台特性
JavaScript(动态脚本语言): 简单易用、免编译、热部署

JSC API

JavaScriptCore.h

这一部分在上一篇文章已经又过一些简单的介绍了。在这里可能会有一些新的理解。

#import <JavaScriptCore/JavaScriptCore.h>

JSContext

就是js的上下文,或者说是js的运行环境

JSValue

JSExport

OC call Js

1、执行js脚本

1
2
3
4
//执行js脚本
JSContext *context = [[JSContext alloc] init];
JSValue *value = [context evaluateScript:@"1+2"];
NSLog(@"%f",[value toDouble]);

2、调用js函数

1
2
3
4
5
6
7
8
9
//调用js函数
//在js上下文中定义一个js函数
//NSString *js = @"function sum(a,b) {return a+b;}";
//[context evaluateScript:js];
//调用这个js函数
//sumA 是js代码中的一个function
JSValue *sum = context[@"sumA"];
JSValue *result = [sum callWithArguments:@[@1,@1]];
NSLog(@"sum(1,2) = %f",[result toDouble]);

3、创建一个js值

1
2
3
4
5
6
7
//创建js值对象
//方法1
JSValue *intVar = [JSValue valueWithInt32:231 inContext:context];
context[@"bar"] = intVar;
[context evaluateScript:@"bar++"];
//方法2
[context evaluateScript:@"var bar = 231;"];

value对应关系,在上文中有介绍

4、捕获错误信息

由于在js 中的错误在native中是不能自动捕捉的。

1
2
3
4
5
//捕获JS中的异常信息
JSContext *context = [[JSContext alloc] init];
context.exceptionHandler = ^(JSContext *ctx ,JSValue *exception){
NSLog(@"%@",exception)
}

Js call Native

1、js call Native via block

1
2
3
4
5
context[@"sum"] = ^(int a, int b){
return a+b;
};
value = [context evaluateScript:@"sum(1,2)"];
//在js 中直接使用 “sum(1,2);”来调用
  • 虽然在oc的代码中block是可以访问自己之外的变量的。但是当我们使用这个block和js互动的时候这个block就不能够调用外面的东西。如果要是用这个context的话,使用[JSContext currentContext]来获取当前的上下文。动态参数:[JSContext currentArguments]

2、js call native via JSExport

在上文中,介绍较多。只是没有仔细的研究具体实现原理。

定义并且实例化一个JSExport类,然后把这个对象放在jscontext。这样就可以通过像操作js对象那样操作这个对象。

但是要注意,在js中没有构造函数。

即使我们通过export向js上下文中注入了一个类,但是我们在js中通过new来实例化这个类的对象是不可行的。即使我们通过context[@"Foo"] = [Foo class]向js注入这么一个类,但是也是不可以的。这可以说是jsbinding的一个缺陷吧。

console等怎么来

上一篇文章中console.log 是可用的,因为js代码实际上也是运行在webview中的,但是如果我们不通过浏览器的话,是没有console这个函数的,这个时候怎么样实现我们的日志输出功能呢?

这个时候如果我们通过一个叫console的类,来实现这个功能就是可以的了。

但是我们其实也可以单独使用一个js本身来模拟这个功能。但我没有去实现过,不敢赘述。

内存泄漏

  • Retain cycle

native 对象 和js对象循环持有,是不能通过GC或者ARC释放的。

这种情况下 Native必须通过JSManagedValue来持有js对象,js可以直接持有。这种情况下,js代码可以正常写,而oc里面的setter方法需要使用[JSManagedValue managedValueWith:]来包装一下。

线程安全

API是线程安全的;

锁的最小粒度是js虚拟机 JSVirtualMachine

如果要并行的运行两个东西,需要把它分到两个虚拟机中

1
2
3
4
5
6
JSVirtualMachine *jsMachineA = [[JSVirtualMachine alloc] init];
JSContext *contextA = [[JSContext alloc] initWithVirtualMachine:jsMachineA];
JSContext *contextB = [[JSContext alloc] initWithVirtualMachine:jsMachineA];
//
JSVirtualMachine *jsMachineB = [[JSVirtualMachine alloc] init];
JSContext *contextC = [[JSContext alloc] initWithVirtualMachine:jsMachineB];

A、B虽然是不容的上下文,但是她们是运行在同一个js虚拟机当中的,他俩在运行的时候是能够保证是一个同步的状态。虽然她们不能直接互相访问,但是她们是能够在Native上面实现一个比较好的通讯。(不需要考虑执行顺序和锁的问题)

A、C是在不同的线程中的,她们是不能很容易通讯的,异步执行,在他们之间的通讯比较麻烦

结束语

这篇文章作为上一篇文章的补充,实在没有那么多的干货,但是作为自己对jsbinding的理解和更加深入的学习,本文的撰写确实帮助我对这一知识的理解更加的系统化。上一篇文章更多的是面相实现,而这篇文章增加了更多对原理的理解。路漫漫其修远兮,吾将上下而求索。

CepheusSun wechat
订阅我的公众号,每次更新我都不一定会告诉你!
坚持原创技术分享,您的支持将鼓励我继续创作!
0%