http://www.jianshu.com/p/1328e15416f3/comments/1724404

javascript目前看来仍是世界上最流行的语言,不管在web、服务端还是客户端都有广泛的应用,很多跨平台方案也采用js来实现,比如著名的reactjs,苹果在iOS7引入了javascriptcore库,提供更简单方便的方式将js接入,iOS7之前要执行js操作只能通过UIWebview中的
stringByEvaluatingJavaScriptFromString方法,而且JavaScriptCore这块的代码开源,可以到这里查看,本文就简单介绍一下JavaScriptCore:

  • Objective-C调用JavaScript
  • JavaScript调用Objective-C
  • 内存管理
  • 多线程

不过在那之前先介绍几个基本概念:

  • JSContext
    一个JSContext实例代表着一个js运行时环境,js代码都需要在一个context上下文内执行,而且JSContext还负责管理js虚拟机中所有对象的生命周期
  • JSValue
    表示一个JavaScript的实体,一个JSValue可以表示很多JavaScript原始类型例如boolean, integers, doubles,甚至包括对象和函数。我们对JS的操作都是通过它,并且每个JSValue都强引用一个context。同时,OC和JS对象之间的转换也是通过它,相应的类型转换如下:

JSValue类型转换
  • JSVirtualMachine
    js代码运行的虚拟机,提供JavaScriptCore执行需要的资源,有自己独立的堆栈以及垃圾回收机制,而且通过锁来实现线程安全,如果需要并发执行js代码,可以创建不同的JSVirtualMachine虚拟机对象来实现;

Objective-C调用JavaScript

oc想要调用js代码的话,先创建一个JSContext对象实例,接着通过evaluateScript加载js代码到context对象中,然后获取js对象,如果为js函数对象,通过callWithArguments调用该js函数,并且可以以数组的方式传递参数。

  //test.js
var appendString = function(name) {
return 'string:' + name;
};
var arr = [1, 2 , 'hello world']; //test.m
NSString *jsPath = [[NSBundle mainBundle] pathForResource:@"test"ofType:@"js"];
NSString *jsContent = [NSString stringWithContentsOfFile:jsPath encoding:NSUTF8StringEncoding error:nil]; JSContext *context = [[JSContext alloc] init];
[context evaluateScript:jsContent]; JSValue *value = [context[@"appendString"] callWithArguments:@[@"hello"]];
JSValue *value1 = context[@"arr"]; NSLog(@"appendString:%@",[value toString] );//appendString:string:hello
NSLog(@"arr:%@",[value1 toArray] );
// arr:(
// 1,
// 2,
// "hello world"
// )

JavaScript调用Objective-C

js调用oc有两种实现方式

  • Blocks方式
    我们可以通过block的方式将oc代码暴露给js,JavaScriptCore会自动将oc block包装在js函数中,我们就可以直接在js中调用该block函数,有点方便~
    JSContext *context = [[JSContext alloc] init];
context[@"sayhi"] = ^(NSString *name) {
NSLog(@"say hi to %@",name);
};
[context evaluateScript:@"sayhi('Greg')"]; //"say hi to Greg"

block.png
  • JSExport协议
    如果你到头文件中去查看JSExport协议,你会发现这个协议其实没有定义任何东西。JavaScriptCore提供这个协议用来将oc的方法跟属性暴露给js调用,其中@property会转换成js的getter和setter方法,实例方法会转换成js函数,而类方法则转换成js中global object的方法。

JSExport

举个例子简单说明一下,我们的PersonProtocol协议定义好要暴露给js的内容:

//定义需要暴露给js的内容,这里我们只暴露personName和queryPersonName接口
@protocol PersonProtocol <JSExport>
@property(nonatomic,copy)NSString *personName;
-(NSString *)queryPersonName;
@end //Person实现PersonProtocol协议,而自己定义的age和queryPersonAge接口不暴露给js
@interface Person : NSObject <PersonProtocol>
@property(nonatomic,assign)NSInteger age;
-(NSInteger)queryPersonAge;
@end @implementation Person
@synthesize personName = _personName; -(NSString *)queryPersonName{
return self.personName;
}
-(NSInteger)queryPersonAge{
return self.age;
}
@end JSContext *context = [[JSContext alloc] init]; //创建Person类的对象,将他赋值给js对象
Person *person=[Person new];
person.personName = @"Greg";
person.age = 27;
context[@"person"]=person; //可以调用获取PersonProtocol暴露的内容
NSString *personName = [[context evaluateScript:@"person.personName"] toString]; //"Greg"
NSString *personName1 = [[context evaluateScript:@"person.queryPersonName()"] toString]; //"Greg" //js无法调用跟age相关的内容
NSInteger age = [[context evaluateScript:@"person.age"] toInt32]; // 0
NSInteger age1 = [[context evaluateScript:@"person.queryPersonAge()"] toInt32]; //0

内存管理

下面我们来说说内存管理方面的问题,我们知道在oc中使用ARC方式管理内存(基于引用计数),但JavaScriptCore中使用的是垃圾回收方式,其中所有的引用都是强引用,我们不必担心其循环引用,js的垃圾回收能够打破这些强引用,通常我们在使用JavaScriptCore中的API时不太需要去关注内存问题,因为这些都会被自动处理好,不过有些情况需要我们注意一下:

  • 在oc对象中存储js的值
    如果在oc对象中存储js的值,需要注意一下不要导致循环引用,看个例子:
//test.js
function ClickHandler(button, callback) {
this.button = button;
this.handler = callback;
}
//test.m
@implement MyButton
-(void)setClickHandler:(JSValue*)handler
{
_onClickHandler = handler; //导致retain cycle
}

上面的代码中可以看出,mybutton的onclickHandler强引用了js的handler,而js的button又强引用了mybutton,这就会导致retain cycle的问题:

retain cycle

在oc中为了打破循环引用我们采用weak的方式,不过在JavaScriptCore中我们采用内存管理辅助对象JSManagedValue的方式,它能帮助引用技术和垃圾回收这两种内存管理机制之间进行正确的转,所以我们可以采用如下方式:

@implement MyButton
-(void)setClickHandler:(JSValue*)handler
{
_onClickHandler = [JSManagedValue managedValueWithValue:handler];
[_context.virtualMachine addManagedReference:_onClickHandler];
}

JSManagedValue本身只弱引用js值,需要调用JSVirtualMachine的addManagedReference:withOwner:把它添加到JSVirtualMachine中,这样如果JavaScript能够找到该JSValue的Objective-C owner,该JSValue的引用就不会被释放。

  • block中捕获JSContexts
    我们知道block会默认强引用它所捕获的对象,如下代码所示,如果block中直接使用context也会造成循环引用,这使用我们最好采用[JSContext currentContext]来获取当前的JSContext:
//bad
JSContext *context = [[JSContext alloc] init];
context[@"callback"] = ^{
JSValue *object = [JSValue valueWithNewObjectInContext:context];
return object;
}; //good
JSContext *context = [[JSContext alloc] init];
context[@"callback"] = ^{
JSValue *object = [JSValue valueWithNewObjectInContext:
[JSContext currentContext]];
return object;
};

多线程

JavaScriptCore中提供的API都是线程安全的,一个JSVirtualMachine在一个线程中,它可以包含多个JSContext,而且相互之间可以传值,为了确保线程安全,这些context在运行的时候会采用锁,可以认为是串行执行。

同一个虚拟机可以相互访问

假如我们需要并发的执行js代码,我们也可以在创建JSContext的时候也指定其所在的虚拟机,不同的虚拟机处于不同的线程中,但是如果在不同的 JSVirtualMachine,上下文并不能直接互相传值,在使用的过程中需要注意一下。

JSVirtualMachine *vm1 = [JSVirtualMachine new];
JSContext *ctxA1 = [[JSContext alloc] initWithVirtualMachine:vm1];
JSContext *ctxA2 = [[JSContext alloc] initWithVirtualMachine:vm1]; JSVirtualMachine *vm2 = [JSVirtualMachine new];
JSContext *ctxB = [[JSContext alloc] initWithVirtualMachine:vm2];

不同一个虚拟机不可以相互访问

参考

https://developer.apple.com/videos/play/wwdc2013/615/
https://developer.apple.com/library/prerelease/ios/documentation/Carbon/Reference/WebKit_JavaScriptCore_Ref/

文/树下的老男孩(简书作者)
原文链接:http://www.jianshu.com/p/1328e15416f3/comments/1724404
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

最新文章

  1. maven编译报错 -source 1.5 中不支持 lambda 表达式
  2. 【前端】【转】JS跨域问题总结
  3. LNMP 部署
  4. Android上dip、dp、px、sp等单位说明
  5. php 利用socket发送GET,POST请求
  6. 在Visual Studio 2010中使用DSL Tool特定领域开发 开篇
  7. 吉哥系列故事——完美队形II
  8. 使用ECharts报表统计公司考勤加班,大家加班多吗?
  9. Tensorflow tflearn 编写RCNN
  10. java虚拟机学习-JVM调优总结(6)
  11. 物流包裹一站式查询(TrackingMore)
  12. Java面试题—初级(2)
  13. 你不可不知的Java引用类型之——Reference源码解析
  14. wamp本地可以访问,远程无法访问,报错:client denied by server configuration
  15. 7.01-beautiful_soup
  16. centos6安装配置zabbix3被控端
  17. node2vec应用记录
  18. Xcode6.1 模拟器路径
  19. http://qurtyy.blog.163.com/blog/static/5744368120130221419244/
  20. python 数字的四舍五入的问题

热门文章

  1. ORA-12520: TNS:listener could not find available handler for requested type of server
  2. Ignite用户配置管理介绍
  3. 机器学习实战笔记(Python实现)-01-K近邻算法(KNN)
  4. 分布式搜索引擎ElasticSearch+Kibana (Marvel插件安装详解)
  5. 【转】JVM运行原理及JVM中的Stack和Heap的实现过程
  6. 【2016-11-15】【坚持学习】【Day26】【通用的SQLHelper】
  7. 【读书笔记《Bootstrap 实战》】3.优化站点资源、完成响应式图片、让传送带支持手势
  8. SPOJ GSS3 Can you answer these queries III[线段树]
  9. python中的常用方法
  10. Oracle trunc()函数的用法