JavaScriptCore 什么是JavaScriptCore?
(一)JavaScriptCore 是一个提供网页Javascript引擎接口的的框架。框架提供了强大的功能,可以实现Swift/Objective-C 和 Javascript 代码之间的交互。(React Native 技术 就是对JavaScriptCore 强大功能的一个实际运用。)
(二)JavaScriptCore是线程安全的,它里头有几个核心组件:JSVirtualMachine, JSContext, 和 JSValue
JSVirtualMachine
1. JSVirtualMachine(虚拟机制类) : JavaScript代码其实是在JSVirtualMachine类提供的环境中执行,通常不必直接对它进行操作,除非需要进行并发执行JavaScript,因为一个JSVirtualMachine是不能再同一时间执行多线程任务的,所以此时需要使用多个虚拟机制。
2. 每一个JSVirtualMachine实例都与自己的堆栈和垃圾回收机制,所以不能再不同的虚拟机制之间传递对象信息,因为虚拟机制的垃圾回收器不知道怎样处理来至其它堆栈的值。需要使用到它有两个主要情况:为了并发执行JavaScript;JavaScript 和 Objecive-C/Swift 之间桥接对象的内存管理(注意使用JSManagedValue)。
JSContext
1. JSContext 是 OC 支持 JavaScript 执行的环境对象, 是运行 JavaScript 代码的环境。OC 语言 和 JavaScript之间的桥接,可以依赖它来实现。一个 JSContext 是一个全局环境的实例,每一个JavaScript context都隶属于一个虚拟机。同虚拟机制类不同,不同context之间是可以进行对象信息传递的(一个虚拟机可以内含多个context,只要将它们放置在同一个虚拟机制下)
2. JSContext 通常与UIWebView一起使用,那应该怎样得到webview中加载的javascript代码呢,不再通过URL拦截,使用JSContext的方法直接取 UIWebView 的 context,然后进行对JS操作:context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
3. 当然也可以使(evaluateScript:)方法添加Javascript代码到context中,或者执行javascript代码。
JSValue
1. JSValue 是 JavaScript值的一个引用实例对象,它可以代表任何JavaScript值类型,一个JSValue实例是搬到在它所依赖的JSContext中的,任何来至这个context中的值都会是一个JSValue类型。
(a)可实现 OC|Swift 到 JavaScript 中基本数据类型的转换,并能够实现原生和JavaScript之间的数据传递,
(b)可以创建JavaScript对象,通过原生的方法或者blocks提供其中的JavaScript函数实现。
2. evaluateScript: 执行javascript代码,返回值JSValue,
(三)下面直接上代码,演示一下如何使用JavaScriptCore框架来实现 Javascript 到 native的调用过程。
/// 给context 中添加 javascript 代码
JSContext *context = [[JSContext alloc] init];
[context evaluateScript:@"var num = 5 + 5"];
[context evaluateScript:@"var names = ['Grace', 'Ada', 'Margaret']"];
[context evaluateScript:@"var triple = function(value) { return value * 3 }"];
[context evaluateScript:@"var sendValueToNative = ({'name1':'1','value1':'a','name2':'2','value2':'b' })"];
/// 利用 context 执行 javascript 代码
JSValue *tripleNum = [context evaluateScript:@"triple(num)"];
NSLog(@"Tripled: %d", [tripleNum toInt32]);
// Tripled: 30
JSValue *value1 = [context evaluateScript:@"sendValueToNative"];
NSLog(@"value1 = %@",[value1 toDictionary]);
//value1 = {name1 = 1; name2 = 2; value1 = a; value2 = b;}
/*
对 JSContext 和 JSValue 实例使用下标的方式我们可以很容易地访问我们之前创建的 context 的任何值。JSContext 需要一个字符串下标,而 JSValue 允许使用字符串或整数标来得到里面的对象和数组:
*/
JSValue *names = context[@"names"];
JSValue *initialName = names[0];
NSLog(@"The first name: %@", [initialName toString]);
// The first name: Grace
/*
JSContext 还有另外一个有用的招数:通过设置上下文的 exceptionHandler 属性,你可以观察和记录语法,类型以及运行时错误。 exceptionHandler 是一个接收一个 JSContext 引用和异常本身的回调处理:
*/
context.exceptionHandler = ^(JSContext *context, JSValue *exception) {
NSLog(@"JS Error: %@", exception);
};
[context evaluateScript:@"function multiply(value1, value2) { return value1 * value2 "];
// JS Error: SyntaxError: Unexpected end of script
/*
1. 现在我们知道了如何从 JavaScript 环境中提取值以及如何调用其中定义的函数。那么反向呢?我们怎样才能从 JavaScript 访问我们在 Objective-C 或 Swift 定义的对象和方法?
2. 让 JSContext 访问我们的本地客户端代码的方式主要有两种:block 和 JSExport 协议。
3. 当一个 Objective-C block 被赋给 JSContext 里的一个标识符,JavaScriptCore 会自动的把 block 封装在 JavaScript 函数里。这使得在 JavaScript 中可以简单的使用 Foundation 和 Cocoa 类,所有的桥接都为你做好了。
4. 由于 block 可以保有变量引用,而且 JSContext 也强引用它所有的变量,为了避免强引用循环需要特别小心。避免保有你的 JSContext 或一个 block 里的任何 JSValue。相反,使用 [JSContext currentContext] 得到当前上下文,并把你需要的任何值用参数传递。
*/
context[@"simplifyString"] = ^(NSString *input) {
NSMutableString *mutableString = [input mutableCopy];
CFStringTransform((__bridge CFMutableStringRef)mutableString, NULL, kCFStringTransformToLatin, NO);
CFStringTransform((__bridge CFMutableStringRef)mutableString, NULL, kCFStringTransformStripCombiningMarks, NO);
return mutableString;
};
NSLog(@"%@", [context evaluateScript:@"simplifyString('안녕하새요!')"]);
context[@"sendValue"] =
^(NSDictionary *objc)
{
return objc[@"name1"];
};
JSValue *value2 = [context evaluateScript:@"sendValue({'name1':'one','value1':'a','name2':'2','value2':'b' })"];
NSLog(@"value2 = %@",[value2 toString]);
// value2 = one
//注册一个匿名函数
JSValue *jsFunction = [context evaluateScript:@" (function(string) { return 'hello objc '+ string })"];
//调用
JSValue *value3 = [jsFunction callWithArguments:@[@"coding is beauty"]];
NSLog(@"value3 = %@",[value3 toString]);
//value3 = hello objc coding is beauty
// 再注册执行一个有名函数,并调用callWithArguments这个方法进行参数传值。(JS里面函数也是对象)
[context evaluateScript:@"function addfun(a,b) {return a+b}"];
JSValue *n = [context[@"addfun"] callWithArguments:@[@2, @3]];
NSLog(@"---%@", @([n toInt32]));//---5
(四)补充。
1. Javascript 的跨平台性非常好,因此所讨论的技术其实就是如何实现Native与Javascript之间的通讯。这样做的使用示例最直接的就是实现一个WebView 和 native 的交互。JavaScriptCore就是为之而设计。
2. UIWebView 中有提供了几个代理方法,利用其中的URL拦截或者js注入等可以实现一些简单的网页到native原生的交互。
3. 罗列几篇参考文章,
//http://nshipster.cn/javascriptcore/
//https://www.raywenderlich.com/124075/javascriptcore-tutorial
//http://www.jianshu.com/p/a329cd4a67ee