logo头像
Snippet 博客主题

JavaScriptCore的基本用法(二)

本文于2337天之前发表,文中内容可能已经过时。

这是JavaScriptCore的第二篇

代理设置(JS调用OC的第二种方法)

h文件


//首先写一个协议  遵守JSExport协议
@protocol JSExportTest <JSExport>
//宏转换下,将JS函数名称指定为Add;
JSExportAs(add, - (NSInteger)add:(NSInteger)a b:(NSInteger)b);
@property (nonatomic, assign) NSInteger sum;

@end


//建一个对象实现这个协议
@interface JSTest : NSObject<JSExportTest>

@end

m文件

@implementation JSTest
@synthesize sum = _sum;
//实现协议方法
- (NSInteger)add:(NSInteger)a b:(NSInteger)b{
return a + b;
}


-(void)setSum:(NSInteger)sum{
NSLog(@"%ld",(long)sum);
_sum = sum;
}

@end

在viewcontroller里面

JSContext *context = [[JSContext alloc] init];
//设置异常处理
self.context.exceptionHandler = ^(JSContext *context, JSValue *exception) {
[JSContext currentContext].exception = exception;
NSLog(@"exception:%@",exception);
};
//将obj添加到context中
scontext[@"obj"] = [][JSTest alloc]init];
//JS里面调用obj方法,并将结果赋值给obj的sum属性
[context evaluateScript:@"obj.sum = obj.add(2,3)"];

JS中进行调用这个对象的方法,并将结果赋值sum。唯一要注意的是OC的函数命名和JS函数命名规则问题。协议中定义的是add: b:,但是JS里面方法名字是add(a,b)。可以通过JSExportAs这个宏转换成JS的函数名字。

异常处理

Objective-C的异常会在运行时被Xcode捕获,而在JSContext中执行的JavaScript如果出现异常,只会被JSContext捕获并存储在exception属性上,而不会向外抛出。时时刻刻检查JSContext对象的exception是否不为nil显然是不合适,更合理的方式是给JSContext对象设置exceptionHandler,它接受的是^(JSContext *context, JSValue *exceptionValue)形式的Block。其默认值就是将传入的exceptionValue赋给传入的context的exception属性:

JSContext *context = [[JSContext alloc] init];
context.exceptionHandler = ^(JSContext *con, JSValue *exception) {
NSLog(@"%@", exception);
con.exception = exception;
};

[context evaluateScript:@"fengzhen = 66"];

//输出:
//  ReferenceError: Can't find variable: fengzhen

无论是把Block传给JSContext对象让其变成avaScript方法,还是把它赋给exceptionHandler属性,在Block内都不要直接使用其外部定义的JSContext对象或者JSValue,应该将其当做参数传入到Block中,或者通过JSContext的类方法+ (JSContext *)currentContext;来获得。否则会造成循环引用使得内存无法被正确释放。

内存管理

OC使用的是ARCJS使用的是垃圾回收机制,js的引用全都是强引用,垃圾回收机制会帮他们打破这种强引用,所以JS不存在循环引用的问题。一般情况下,OCJS对象之间内存管理都无需我们去关心。不过还是有几个注意点需要我们去留意下。

不要在block里面直接使用context,或者使用外部的JSValue对象。

JSContext *context = [[JSContext alloc] init];
//设置异常处理
self.context.exceptionHandler = ^(JSContext *context, JSValue *exception) {
//直接这么使用是错误的
//context.exception = exception;
[JSContext currentContext].exception = exception;
NSLog(@"exception:%@",exception);
};

OC对象不要用属性直接保存JSValue对象,因为这样太容易造成循环引用。

下面的例子:

#import <Foundation/Foundation.h>
#import <JavaScriptCore/JavaScriptCore.h>
//首先写一个协议  遵守JSExport协议
@protocol JSExportTest <JSExport>
//宏转换下,将JS函数名称指定为Add;
JSExportAs(add, - (NSInteger)add:(NSInteger)a b:(NSInteger)b);
@property (nonatomic, strong) JSValue *value;

@end


//建一个对象实现这个协议
@interface JSTest : NSObject<JSExportTest>

@end
#import "JSTest.h"

@implementation JSTest
@synthesize value = _value;
//实现协议方法

-(void)setValue:(JSValue *)value{
_value = value;
}
@end

viewController里面

JSContext *context = [[JSContext alloc]init];
context.exceptionHandler = ^(JSContext *j, JSValue *v){
NSLog(@"%@",j.exception);
};
[context evaluateScript:@"function callback(){return 'hello world'};function setObj(obj){this.obj = obj;obj.value = callback}"];
[context[@"setObj"] callWithArguments:@[self.testObj]];

调用JS方法,进行赋值,JS对象保留了传进来的obj,最后,JS将自己的回调callback赋值给了obj,方便obj下次回调给JS;由于JS那边保存了obj,而且obj这边也保留了JS的回调。这样就形成了循环引用。
为了打破这种强引用,apple有一个JSManagedValue 的类,官方的原话:

The JSManagedValue's JavaScript value is reachable from JavaScript

The owner of the managed reference is reachable in Objective-C. Manually adding or removing the managed reference in the JSVirtualMachine determines reachability.

JSManagedValue 帮助我们保存JSValue,里面保存的JS对象必须在JS中存在,同时 JSManagedValue 的owner在OC中也存在.因此我们把代理的m文件修改如下:

-(void)setValue:(JSValue *)value{
//    由于是回掉的关系  obj保存了JS的回掉, js也保存了obj,这样就形成了循环引用
//    JSManageValue帮助我们保存了JSValue,哪里保存的js对象在js中存在。 JSMangerValue的owner在OC中也存在。

JSManagedValue *mavalue = [JSManagedValue managedValueWithValue:value];
//建立弱引用关系
[[[JSContext  currentContext] virtualMachine] addManagedReference:mavalue withOwner:self];
_value = value;
}

不要在不同的 JSVirtualMachine 之间进行传递JS对象。

一个JSVirtualMachine可以运行多个context,由于都是在同一个堆内存和同一个垃圾回收下,所以相互之间传值是没问题的。但是如果在不同的 JSVirtualMachine传值,垃圾回收就不知道他们之间的关系了,可能会引起异常。

JavaScriptCore线程是安全的。

每个context运行的时候通过lock关联的JSVirtualMachine。如果要进行并发操作,可以创建多个JSVirtualMachine实例进行操作。

上一篇