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

簡(jiǎn)述

說(shuō)到react native實(shí)現(xiàn)的js-oc之間的互相通信,有的同學(xué)很快就想到了javascriptcore,但是這個(gè)javascriptcore框架是iOS7才推出的,因此react native只是用了iOS自帶的javascriptcore作為js的解析引擎,但沒有用到j(luò)avascriptcore框架提供的js與oc互調(diào)的特性,而是自己實(shí)現(xiàn)了一套機(jī)制,這套機(jī)制可以通用于所有JS引擎上,在沒有JavaScriptCore的情況下用webview作為解析引擎,用于兼容iOS7以下沒有JavascriptCore的版本。

iOS7之前,iOS app與javascript的交互只有一種方式,那就是UIWebView暴露的stringByEvaluatingJavaScriptFromString:方法,你可以使用這個(gè)簡(jiǎn)單的api在web視圖上顯示html文檔。iOS7以后,開發(fā)者可以深入了解javascript運(yùn)行時(shí),可以訪問(wèn)變量、接收回調(diào)、共享oc對(duì)象,這樣就有了oc-js交互的可能。

oc-js通信的簡(jiǎn)單實(shí)現(xiàn)

簡(jiǎn)單變量值修改

javascript運(yùn)行在一個(gè)JSVirtualMachine類呈現(xiàn)的虛擬機(jī)中,JSVirtualMachine是輕量級(jí)的,但重要的一點(diǎn),可以實(shí)例化多個(gè)JSVirtualMachine來(lái)支持多線程的javascript,每個(gè)JSVirtualMachine可以是任意數(shù)量的JSContexts,一個(gè)JSContext對(duì)應(yīng)一個(gè)JavaScript運(yùn)行時(shí)環(huán)境,并提供了一些關(guān)鍵功能,兩個(gè)是特別重要的快速訪問(wèn):訪問(wèn)全局對(duì)象,執(zhí)行腳本的能力。

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

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

控制臺(tái)信息:

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

接著執(zhí)行下面的代碼

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

控制臺(tái)信息:

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

執(zhí)行功能需求

想要用javascript執(zhí)行環(huán)境做些有用的事情,就要能執(zhí)行一些javascript 代碼,跟UIWebview不同,JSContext像是一張白紙,需要?jiǎng)?chuàng)建函數(shù)并執(zhí)行它。

[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);

控制臺(tái)信息:

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方法會(huì)取出數(shù)組中的參數(shù),但是要確保接收者是一個(gè)有效的javascript方法,否則將會(huì)失效,比如我們修改下square方法,會(huì)得到下面的輸出信息

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

除了普通賦值外,還可以將一個(gè)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);

控制臺(tái)信息:

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

通過(guò)這種方式,可以執(zhí)行oc里的回調(diào)處理,同時(shí)有些注意點(diǎn),應(yīng)該避免從block中獲取JSValue或者JSContext,因?yàn)檫@些對(duì)象的循環(huán)引用可能會(huì)導(dǎo)致泄露。

對(duì)象數(shù)據(jù)同步

JSValue包裝了各種JavaScript的值,包括基本數(shù)據(jù)類型和對(duì)象,如下表:

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

從表中可以看到兩點(diǎn),一時(shí)沒有可變類型,二是傳遞給JSContext對(duì)象時(shí),如果對(duì)象類型不是NSNull NSString,NSNumber NSDictionary,NSArray、NSDate或NSBlock,JavaScriptCore將相關(guān)的類層次結(jié)構(gòu)導(dǎo)入到JavaScript執(zhí)行上下文并創(chuàng)建出等價(jià)類和原型。我們可以使用JSExport協(xié)議暴露部分定制類,JavaScript將會(huì)創(chuàng)建一個(gè)包裝對(duì)象另透?jìng)?。因此一個(gè)對(duì)象可以共享讀取或改變。

示例:

@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

執(zhí)行下面的代碼

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);

控制臺(tái)打印信息如下:

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

當(dāng)然你也可以只暴露一個(gè)屬性,比如注銷到下面這個(gè),如:

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

我們?cè)倏聪逻\(yùn)行結(jié)果:

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

可以看出當(dāng)想通過(guò)evaluateScript方法改變thing對(duì)象的name屬性的值時(shí),由于這個(gè)屬性沒有暴露出來(lái),導(dǎo)致修改不成功。

這篇文章只能幫助你了解javascriptcore的皮毛,想要了解更多可能閱讀下JavaScriptCore的API源碼.

聲明:本文主要翻譯了Owen Mathews的文章,有些內(nèi)容做了修改,也加了一些自己的理解,測(cè)試結(jié)果全部由自主實(shí)現(xiàn),僅供大家參考。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容