JavaScriptCore 简介
转自http://esoftmobile.com/2013/06/19/integrating-javascript-into-native-applications/
Integrating JavaScript into Native Applications
JavaScriptCore 简介
iOS7 中新加入的 JavaScriptCore.framework 可能被大多数开发人员所忽略,但是如果你之前就在项目中用过自己编译JavaScriptCore来处理 JavaScript,那么你需要重新关注一下 JavaScriptCore.framework。
JavaScriptCore 是苹果 Safari 浏览器的 JavaScript 引擎,或许你之前听过 Google 的 V8 引擎,在 WWDC 上苹果演示了最新的 Safari,据说 JavaScript 处理速度已经大大超越了 Google 的 Chrome,这就意味着 JavaScriptCore 在性能上也不输 V8 了。
其实 JavaScriptCore.framework 在 OS X 平台上很早就存在的,不过接口都是纯 C 语言的,而在 iOS 平台,苹果没有开放该 framework,所以不少需要在 iOS app 中处理 JavaScript 的都得自己从开源的 WebKit 中编译出 JavaScriptCore.a,接口也是纯 C 语言的。可能是苹果发现越来越多的程序使用了自编译的 JavaScriptCore,干脆做个顺水人情将 JavaScriptCore.framework 开放了,同时还提供了 Objective-C 的接口。
Objetive-C -> JavaScript
@import JavaScriptCore;
int main() {
JSContext *context = [[JSContext alloc] init];
JSValue *result = [context evaluateScript:@"2 + 2"];
NSLog(@"2 + 2 = %d", [result toInt32]);
return0;
}
这里就需要介绍一下概念了,首先是JSContext,一个 Context 就是一个 JavaScript 代码执行的环境,也叫作用域。既然是作用域,那作用域可以是有大有小的:
var globalVar = "level0"
functionfun1(){
var value1 = "level1";
var fun2 = function(){
var value2 = "level2";
}
}
在上面的 JS 代码中,一共有三个 JSContext,最外层的 Context 包含 globalVar 对象和 fun1 函数,其实该层 Context 包含一个隐性的对象,叫做:GlobalObject(在浏览器环境下该对象就是 Window),所有属于该 Context 的对象其实是 GloalObject 的属性。fun1 函数内属于第二个 Context,fun2 内为第三个 Context。我们只能在相应的 Context 下去执行对应的代码段。也就是你不能用最外层的 JSContext 直接调用 evaluateScript 方法执行 fun2 函数。但是不管有多少个 Context,他们的 GlobalObject 都是指向的一个对象。
大家知道 JS 里面是弱类型的,也就是只有在代码执行时才能知道一个变量具体是什么类型,而 Objective-C 是强类型了,为了处理这种类型差异,JSValue就被引入了。下面是 Objective-C 和 JavaScript 中类型的对照表:
Objective-C type |
JavaScript type |
nil |
undefined |
NSNull |
null |
NSString |
string |
NSNumber |
number, boolean |
NSDictionary |
Object object |
NSArray |
Array object |
NSDate |
Date object |
NSBlock * |
Function object * |
id ** |
Wrapper object ** |
Class * |
Constructor object * |
JSValue 的作用就是在 Objective-C 对象和 JavaScript 对象之间起转换作用:
//covert Objective-C Object to JavaScript Object
JSValue *jsObject = [JSValue valueWithObject:objcObject inContext:context];
//Covert JavaScript Object to Objective-C Object
id objcObject = [jsObject toObject];
更多关于在 Objective-C 环境下调用 JavaScript 的实例代码,推荐查看 WebKit 开源项目中 JavaScriptCore 的单元测试代码: https://github.com/WebKit/webkit/blob/master/Source/JavaScriptCore/API/tests/testapi.mm
JavaScript -> Objective-C
可以通过两种方式在 JavaScript 中调用 Objective-C:
- Blocks: 对应 JS 函数
- JSExport 协议: 对应 JS 对象
Blocks
context[@"makeUIColor"] = ^(NSDictionary *rgbColor){
float red = [rgbColor[@"red"] floatValue];
float green = [rgbColor[@"green"] floatValue];
float blue = [rgbColor[@"blue"] floatValue];
return [UIColor colorWithRed:(red / 255.0)
green:(green / 255.0)
blue:(blue / 255.0)
alpha:1];
};
JSValue *color = [context evaluateScript:@"makeUIColor({red: 50, green: 150, blue: 250})"];
NSLog(@"color:%@",[color toObject]);
通过 Blocks 实现 JS 调用 Objective-C 时有两点需要注意的问题:
- 不要在 Block 中直接引用使用外面的 JSContext 对象,如果想获取当前的 Context 对象,应该用[JSContext currentContext];,这样来避免循引用问题。
- 不要在 Block 中直接使用外面的 JSValue 对象,如果需要,把 JSValue 当做参数来传进 Block 中。
JSExport
JSExport 是一个协议,很方便的让 JavaScript 能够访问和操作 Objective-C 对象。
#import <objc/runtime.h>
@import JavaScriptCore;
@protocolUIButtonExport <JSExport>
- (void)setTitle:(NSString *)title forState:(UIControlState)state;
@end
- (void)viewDidLoad{
[super viewDidLoad]
class_addProtocol([UIButtonclass], UIButtonExpert);
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
[button setTitle:@"Hello Objective-C" forState:UIControlStateNormal];
button.frame = CGRectMake(20, 40, 280, 40);
[self.view addSubview:button];
JSContext *context = [[JSContext alloc] init];
context[@"button"] = button;
[context evaluateScript:@"button.setTitleForState('Hello JavaScript', 0)"];
}
上面代码中,我们申明一个 UIButtonExport 协议,该协议继承于 JSExport,并将setTitle:forState:方法开放到该协议中(只有 JSExport 协议中的方法才能被 JavaScript 识别),然后通过运行时让 UIButton 遵循 UIButtonExport 协议。这样你就可以在 JS 中为 Button 设置 title 了,需要说明一点的是,在 JS 中方法的命名规则与 Objective-C 中有点不一样,如 Objective-C 中的方法-(void)setX:(id)x Y:(id)y Z:(id)z;,加入到 JSExport 协议中,在 JS 中调用就得是setXYZ(x, y, z);,当然如果你不想根据这种命名转换规则,你也可以通过 JSExport.h 中的方法来修改:
#define JSExportAs(PropertyName, Selector) \
@optional Selector __JS_EXPORT_AS__##PropertyName:(id)argument; @required Selector
#endif
如 setX:Y:Z 方法,我们可以给他重命名,让 JS 中通过 set3D(x,y,z) 来调用
JSExportAs(set3D,
- (void)setX:(id)x Y:(id)y Z:(id)z
);
思考: 理论上我们可以通过运行时,让 Foundation 和 UIKit 等 framework 中所有的类的属性和方法遵循 JSExport 协议,这样就可以直接在 JS 中使用这些 Objective-C 的类。
内存管理
Objective-C 使用 ARC,在 JavaScript 中使用是垃圾回收,并且在 JS 中所有的引用都是强引用(strong),当然 JavaScriptCore 新增的 Obj-C 的接口为你省去了很多处理,你在使用的时候只需要注意两点就行了:
- 将 JSValue 对象存储到 Objective-C 对象中;
- 将 JS 字段添加到 Objective-C 对象。
functionClickHandler(button, callback) {
this.button = button;
this.button.onClickHandler = this;
this.handleEvent = callback;
};
在上面的 js 代码中,我们为 button 添加 onclick 处理事件,在 Objective-C 对用的 Button 类中,我们需要保存该 onclick handler,以便在按钮点击时调用该 handler。
@implementationMyButton
- (void)setOnClickHandler:(JSValue *)handler
{
_onClickHandler = handler; // Retain cycle
}
@end
如果我们直接来保存到 handler,就会出现内存泄露,因为 JS 中引用 button 对象是强引用,如果 Button 也用强引用来保存 JS 中的 handler,这就导致了 Retain cycle。我们没法改变 JavaScript 中的强引用机制,只能在 Objective-C 中来处理,没错,在 Objective-C 中弱引用 js handler,但是弱引用 handler,万一在我点击 Button 调用 click 事件时, onclick handler 已经被释放了怎么办? 来看看 JavaScriptCore 是怎么做的:
@implementationMyButton
- (void)setOnClickHandler:(JSValue *)handler
{
_onClickHandler = [JSManagedValue managedValueWithValue:handler];
[_context.virtualMachine addManagedReference:_onClickHandler
withOwner:self]
}
@end
JavaScriptCore 中引入了JSManagedValue类型,该类型主要是作为一个引用桥接,将 JSValue 转为 JSManagedValue 类型后,可以添加到 JSVirtualMachine 对象中,这样能够保证你在使用过程中 JSValue 对象不会被释放掉,当你不再需要该 JSValue 对象后,从 JSVirtualMachine 中移除该 JSManagedValue 对象,JSValue 对象就会被释放并置空。
大家不要被这么多对象类型搞晕了,简单一点说,JSVirtualMachine就是一个用于保存弱引用对象的数组,加入该数组的弱引用对象因为会被该数组 retain,所以保证了使用时不会被释放,当数组里的对象不再需要时,就从数组中移除,没有了引用的对象就会被系统释放。
到这里要介绍的东西就差不多了,苹果这次开放了 JavaScriptCore,其实给程序开发提供了无限的可能,Objective-C 和 JavaScript 相结合,也一定能够产生出更多的开发模式。如果想继续了解 JavaScriptCore,再次推荐看看 WebKit 项目组 JavaScriptCore 单元测试用例, 还可以研究一下本文中没有介绍的 JavaScriptCore 的 C 接口。
JavaScriptCore 简介的更多相关文章
- Integrating JavaScript into Native Applications
JavaScriptCore 简介 iOS7 中新加入的 JavaScriptCore.framework 可能被大多数开发人员所忽略,但是如果你之前就在项目中用过自己编译JavaScriptCore ...
- iOS与JS开发交互总结
hybrid.jpg 前言 Web 页面中的 JS 与 iOS Native 如何交互是每个 iOS 猿必须掌握的技能.而说到 Native 与 JS 交互,就不得不提一嘴 Hybrid. Hybri ...
- 深入理解react-native
欢迎转载,请支持原创,保留原文链接:http://blog.ilibrary.me http://blog.ilibrary.me/2016/12/25/react-native-internal ( ...
- Weex 简介
weex简介 Weex 是一套简单易用的跨平台开发方案,能以 web 的开发体验构建高性能.可扩展的 native 应用,为了做到这些,Weex 与 Vue 合作,使用 Vue 作为上层框架,并遵循 ...
- Mach-O简介及实际应用
一.前言 在正题开始之前,我们先来聊聊iOS中的hook技术.一谈到hook,很多人首先想到的是runtime,runtime确实强大,但是它存在很多局限性: 1).侵入性:一旦hook了某个类的 ...
- ASP.NET Core 1.1 简介
ASP.NET Core 1.1 于2016年11月16日发布.这个版本包括许多伟大的新功能以及许多错误修复和一般的增强.这个版本包含了多个新的中间件组件.针对Windows的WebListener服 ...
- MVVM模式和在WPF中的实现(一)MVVM模式简介
MVVM模式解析和在WPF中的实现(一) MVVM模式简介 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在 ...
- Cassandra简介
在前面的一篇文章<图形数据库Neo4J简介>中,我们介绍了一种非常流行的图形数据库Neo4J的使用方法.而在本文中,我们将对另外一种类型的NoSQL数据库——Cassandra进行简单地介 ...
- REST简介
一说到REST,我想大家的第一反应就是“啊,就是那种前后台通信方式.”但是在要求详细讲述它所提出的各个约束,以及如何开始搭建REST服务时,却很少有人能够清晰地说出它到底是什么,需要遵守什么样的准则. ...
随机推荐
- 日常-acm-鸡兔同笼
已知鸡和兔总数量n,总腿数m.输入n和m,依次输出鸡的数量和兔的数量.如果无解,则输出No answer. 样例输入: 14 32 样例输出: 12 2 样例输入: 10 16 样例输出: No an ...
- 唱吧APP产品体验报告
- netbackup如何手动获取主机ID证书。
如何手动获取主机ID证书. 文章:100039650 最后发布:2017-09-21 评分: 20 11 产品:NetBackup 问题 从NetBackup V8.1开始,管理员需要在证书颁发 ...
- vue 修改端口
- Windows环境下使用Apache+mod
1.安装Python和Apache. 2.安装mod_wsgi后获得wsgi.so,并将wsgi.so放到Apache的modules文件夹下. 3.安装webpy. 4.打开httpd.conf(在 ...
- 2018.4.28 基于java的聊天系统(带完善)
Java聊天系统 1.Socket类 Socket(InetAddress address, int port) 创建一个流套接字并将其连接到指定 IP 地址的指定端口号. Socket(String ...
- mysql如何让自增id从1开始设置方法
有两种方式 第一种: 如果表中数据没有用.如果直接删除数据,自动增长ID还是不会从1开始的,可以利用“清空数据表”.这样自动增长ID也将会从1开始. 清空表的sql如下 truncate table ...
- Python -- 函数之推导式
5.12 推导式 l = [] for i in range(1,11): l.append(i) print(l) # 用列表推导式 (一行搞定) l = [i for i in range(1,1 ...
- ValidForm验证表单
在做项目时,要求熟悉项目中验证表单的插件,所以学习一下validForm这个插件 http://validform.rjboy.cn/document.html#validformObject
- NSXMLParser
NSXMLParser的使用 2011-05-05 15:50:17| 分类: 解析|字号 订阅 NSXMLParser解析xml格式的数据 用法如下: 首先,NSXMLParser必须继续 ...