React Native探索系列二——回顾JavaScriptCore

简述

说到react native实现的js-oc之间的互相通信,有的同学很快就想到了javascriptcore,但是这个javascriptcore框架是iOS7才推出的,因此react native只是用了iOS自带的javascriptcore作为js的解析引擎,但没有用到javascriptcore框架提供的js与oc互调的特性,而是自己实现了一套机制,这套机制可以通用于所有JS引擎上,在没有JavaScriptCore的情况下用webview作为解析引擎,用于兼容iOS7以下没有JavascriptCore的版本。

iOS7之前,iOS app与javascript的交互只有一种方式,那就是UIWebView暴露的stringByEvaluatingJavaScriptFromString:方法,你可以使用这个简单的api在web视图上显示html文档。iOS7以后,开发者可以深入了解javascript运行时,可以访问变量、接收回调、共享oc对象,这样就有了oc-js交互的可能。

oc-js通信的简单实现

简单变量值修改

javascript运行在一个JSVirtualMachine类呈现的虚拟机中,JSVirtualMachine是轻量级的,但重要的一点,可以实例化多个JSVirtualMachine来支持多线程的javascript,每个JSVirtualMachine可以是任意数量的JSContexts,一个JSContext对应一个JavaScript运行时环境,并提供了一些关键功能,两个是特别重要的快速访问:访问全局对象,执行脚本的能力。

JSContext *context = [[JSContext alloc] initWithVirtualMachine:[[JSVirtualMachine alloc] init]];
context[@"a"] = @5;

JSValue *aValue = context[@"a"];
double a = [aValue toDouble];
NSLog(@"%.0f", a);

控制台信息:

2016-01-20 14:50:56.897 XWTestDemo[3927:1459293] 5

接着执行下面的代码

[context evaluateScript:@"a=10"];
JSValue *newValue = context[@"a"];
NSLog(@"%.0f", [newValue toDouble]);

控制台信息:

2016-01-20 14:56:19.590 XWTestDemo[4011:1489852] 10

执行功能需求

想要用javascript执行环境做些有用的事情,就要能执行一些javascript 代码,跟UIWebview不同,JSContext像是一张白纸,需要创建函数并执行它。

[context evaluateScript:@"var square = function(x) {return x*x;}"];
JSValue *squareFunction = context[@"square"];
NSLog(@"%@", squareFunction);
JSValue *aSquared = [squareFunction callWithArguments:@[context[@"a"]]];
NSLog(@"a^2: %@", aSquared);
JSValue *nineSquared = [squareFunction callWithArguments:@[@9]];
NSLog(@"9^2: %@", nineSquared);

控制台信息:

2016-01-20 15:06:37.272 XWTestDemo[4171:1552767] function (x) {return x*x;}
2016-01-20 15:06:37.272 XWTestDemo[4171:1552767] a^2: 100
2016-01-20 15:06:37.273 XWTestDemo[4171:1552767] 9^2: 81

JSValue的callWithArguments方法会取出数组中的参数,但是要确保接收者是一个有效的javascript方法,否则将会失效,比如我们修改下square方法,会得到下面的输出信息

2016-01-20 15:24:05.194 XWTestDemo[4418:1646313] a^2: undefined
2016-01-20 15:24:05.195 XWTestDemo[4418:1646313] 9^2: undefined

除了普通赋值外,还可以将一个oc的block代码块赋给JSContext,如下:

context[@"factorial"] = ^(int x) {
    int factorial = 1;
    for (; x > 1; x--) {
        factorial *= x;
    }
    return factorial;
};
[context evaluateScript:@"var fiveFactorial = factorial(5);"];
JSValue *fiveFactorial = context[@"fiveFactorial"];
NSLog(@"5! = %@", fiveFactorial);

控制台信息:

2016-01-20 15:35:11.325 XWTestDemo[4668:1705564] 5! = 120

通过这种方式,可以执行oc里的回调处理,同时有些注意点,应该避免从block中获取JSValue或者JSContext,因为这些对象的循环引用可能会导致泄露。

对象数据同步

JSValue包装了各种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

从表中可以看到两点,一时没有可变类型,二是传递给JSContext对象时,如果对象类型不是NSNull NSString,NSNumber NSDictionary,NSArray、NSDate或NSBlock,JavaScriptCore将相关的类层次结构导入到JavaScript执行上下文并创建出等价类和原型。我们可以使用JSExport协议暴露部分定制类,JavaScript将会创建一个包装对象另透传。因此一个对象可以共享读取或改变。

示例:

@protocol ThingJSExports <JSExport>
@property (nonatomic, copy) NSString *name;
@property (nonatomic )      NSInteger   number;
@end

@interface Thing : NSObject <ThingJSExports>
@property (nonatomic, copy) NSString *name;
@property (nonatomic) NSInteger number;
@end

@implementation Thing
- (NSString *)description {
    return [NSString stringWithFormat:@"%@: %d", self.name, self.number];
}
@end

执行下面的代码

Thing *thing = [[Thing alloc] init];
thing.name = @"Joan";
thing.number = 5;
context[@"thing"] = thing;
JSValue *thingValue = context[@"thing"];
NSLog(@"Thing: %@", thing);
NSLog(@"Thing JSValue: %@", thingValue);

thing.name = @"Betty";
thing.number = 8;
NSLog(@"Thing: %@", thing);
NSLog(@"Thing JSValue: %@", thingValue);

JSContext *context = [[JSContext alloc] initWithVirtualMachine:[[JSVirtualMachine alloc] init]];
[context evaluateScript:@"thing.name = \"Carlos\"; thing.number = 5"];
NSLog(@"Thing: %@", thing);
NSLog(@"Thing JSValue: %@", thingValue);

控制台打印信息如下:

2016-01-20 16:53:24.882 XWTestDemo[5946:2182910] Thing: Joan:5
2016-01-20 16:53:24.883 XWTestDemo[5946:2182910] Thing JSValue: Joan:5
2016-01-20 16:53:37.476 XWTestDemo[5946:2182910] Thing: Betty:8
2016-01-20 16:53:37.477 XWTestDemo[5946:2182910] Thing JSValue: Betty:8
2016-01-20 16:53:39.130 XWTestDemo[5946:2182910] Thing: Carlos:5
2016-01-20 16:53:39.130 XWTestDemo[5946:2182910] Thing JSValue: Carlos:5

当然你也可以只暴露一个属性,比如注销到下面这个,如:

//@property (nonatomic, copy) NSString *name;

我们再看下运行结果:

2016-01-20 16:55:39.017 XWTestDemo[6002:2194733] Thing: Joan:5
2016-01-20 16:55:39.017 XWTestDemo[6002:2194733] Thing JSValue: Joan:5
2016-01-20 16:55:57.714 XWTestDemo[6002:2194733] Thing: Betty:8
2016-01-20 16:55:57.714 XWTestDemo[6002:2194733] Thing JSValue: Betty:8
2016-01-20 16:55:59.941 XWTestDemo[6002:2194733] Thing: Betty:5
2016-01-20 16:55:59.941 XWTestDemo[6002:2194733] Thing JSValue: Betty:5

可以看出当想通过evaluateScript方法改变thing对象的name属性的值时,由于这个属性没有暴露出来,导致修改不成功。

这篇文章只能帮助你了解javascriptcore的皮毛,想要了解更多可能阅读下JavaScriptCore的API源码.

声明:本文主要翻译了Owen Mathews的文章,有些内容做了修改,也加了一些自己的理解,测试结果全部由自主实现,仅供大家参考。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,393评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,790评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,391评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,703评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,613评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,003评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,507评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,158评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,300评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,256评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,274评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,984评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,569评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,662评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,899评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,268评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,840评论 2 339

推荐阅读更多精彩内容