说说JavaScriptCore

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
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

时间: 2024-11-10 00:51:27

说说JavaScriptCore的相关文章

ios7 JavaScriptCore.framework

以前想要oc调用js代码一般是通过uiwebview的stringByEvaluatingJavaScriptFromString方法,或是直接使用JavaScriptCore引擎,例如https://github.com/jfahrenkrug/AddressBookSpy. 但是,Apple正式发布了新的iOS 7系统,引入了JavaScriptCore.framework  ,最大最直观的改变在于界面变得小清新范了,我也提到<iOS,你真的越来越像Android了>.不过对于移动开发者来

JavaScriptCore全面解析 (下篇)

殷源,专注移动客户端开发,微软Imagine Cup中国区特等奖获得者,现就职于腾讯. 接JavaScriptCore全面解析 (上篇) 六. JSExport JSExport协议提供了一种声明式的方法去向JavaScript代码导出Objective-C的实例类及其实例方法,类方法和属性. 1. 在JavaScript中调用native代码 两种方式: Block JSExport Block的方式很简单,如下: context[@"add"] = ^(NSInteger a, N

iOS js oc相互调用JavaScriptCore(一)

原址:http://blog.csdn.net/lwjok2007/article/details/47058101 1.普通调用 从iOS7开始 苹果公布了JavaScriptCore.framework 它使得JS与OC的交互更加方便了. 下面我们就简单了解一下这个框架 首先我导入framework 方法如下 点击Linked Frameworks and Libraries 的添加后 选择 JavaScriptCore.framework 选中JavaScriptCore.framewor

转载 【iOS开发】网页JS与OC交互(JavaScriptCore) OC -----&gt;JS

目标 本文介绍利用苹果在iOS7时发布的JavaScriptCore.framework框架进行js与OC的交互.我们想要达到的目标是: OC调用网页上的js方法 网页js调用APP中的OC方法 JavaSciptCore.framework框架介绍 JavaScriptCore是webkit的一个重要组成部分,主要是对js进行解析和提供执行环境.具体介绍请看这篇简书的文章:JavaScriptCore 使用 准备环境 创建一个名为JS与OC交互Demo的iOS工程.然后在storyboard添

iOS js oc相互调用(JavaScriptCore)(二)

http://blog.csdn.net/lwjok2007/article/details/47058795 上节地址 http://blog.csdn.net/lwjok2007/article/details/47058101 接着上节我们讲到的iOS调用js 下来我们使用js调用iOS js调用iOS分两种情况 一,js里面直接调用方法 二,js里面通过对象调用方法 首先我们看第一种,直接调用方法. 其中用到了iOS的block 上代码 [objc] view plaincopy -(v

iOS js oc相互调用(JavaScriptCore)

从iOS7开始 苹果公布了JavaScriptCore.framework 它使得JS与OC的交互更加方便了. 下面我们就简单了解一下这个框架 首先我导入framework 方法如下 点击Linked Frameworks and Libraries 的添加后 选择 JavaScriptCore.framework 选中JavaScriptCore.framework后 点击右下角Add 添加完成 好 创建完成之后我们导入一下头文件 #import <JavaScriptCore/JavaScr

JavaScriptCore框架介绍

http://www.cocoachina.com/ios/20140409/8127.html 这个框架其实只是基于webkit中以C/C++实现的JavaScriptCore的一个包装,在旧版本iOS开发中,很多开发者也会自行将webkit的库引入项目编译使用.不过虽然iOS7把它当成了标准库,可惜目前,我还没有在Apple Developer中找到像那样的官方文档介绍这个框架的具体使用方法. JavaScriptCore中的类 在项目中引入JavaScriptCore后,链到头文件中,除了

JavaScriptCore使用

摘录: http://ifelseboy.com/2015/11/05/javascriptcore/ http://justsee.iteye.com/blog/2036713 http://blog.iderzheng.com/ios7-objects-management-in-javascriptcore-framework/ iOS7以前我们对JS的操作只有webview里面一个函数 stringByEvaluatingJavaScriptFromString,JS对OC的回调都是基于

OC和JS的交互---JavaScriptCore

JavaScriptCore的简单介绍 JavaScriptCore是iOS7提供的原生框架,可以让Objective-C和JavaScript提供简单直接的交互. JavaScriptCore的使用 导入JavaScriptCore.framework 在需要使用的类中引入头文件 #import <JavaScriptCore/JavaScriptCore.h> 点开头文件,我们可以看到 #import "JSContext.h" #import "JSValu