React Native源碼中JavaScriptCore詳解

前言

最近在看React Native源碼的時(shí)候狈蚤,發(fā)現(xiàn)內(nèi)部使用了一個(gè)JavaScriptCore框架绝葡,可以用來(lái)進(jìn)行OC和JS互相調(diào)用叫胖,借機(jī)寫(xiě)了一篇OC和JS互相調(diào)用招驴,為后續(xù)想了解React Native源碼的同學(xué)埋下伏筆篙程,當(dāng)然后期也會(huì)把自己所了解的React Native源碼實(shí)現(xiàn)跟大家分享。

如果喜歡我的文章别厘,可以關(guān)注我微博:袁崢Seemygo

一虱饿、JavaScriptCore常用的類(lèi)

  •  JavaScriptCore作用:JavaScriptCore是蘋(píng)果原生API,用來(lái)JS和OC交互的丹允。
    
  • JSContext: JS運(yùn)行環(huán)境郭厌,用它去執(zhí)行JS代碼袋倔,并且通過(guò)它去獲取JS里的數(shù)據(jù)
  • JSValue: 用于接收J(rèn)S中獲取的數(shù)據(jù)類(lèi)型雕蔽,可以是任一對(duì)象,方法宾娜。

二批狐、OC調(diào)用JS

  • 本質(zhì):JS代碼中已經(jīng)定義好變量和方法,通過(guò)OC去獲取前塔,并且調(diào)用
  • 步驟:
    • 1.創(chuàng)建JS運(yùn)行環(huán)境
    • 2.執(zhí)行JS代碼
    • 3.獲取JS數(shù)據(jù)(變量嚣艇,方法)
    • 4.使用JS數(shù)據(jù),方法

2.1 獲取定義在JS中的變量

  • 可以直接通過(guò)OC修改JS中變量的值
#pragma mark - 獲取JS中定義的變量
- (void)getJSVar
{
    // JS代碼
    NSString *jsCode = @"var arr = [1,2,3]";
    
    // 創(chuàng)建JS運(yùn)行環(huán)境
    JSContext *ctx = [[JSContext alloc] init];
    
    // 執(zhí)行JS代碼
    [ctx evaluateScript:jsCode];
    
    // 因?yàn)樽兞恐苯佣x在JS中华弓,所以可以直接通過(guò)JSContext獲取食零,根據(jù)變量名稱(chēng)獲取,相當(dāng)于字典的Key
    // 只有先執(zhí)行JS代碼寂屏,才能獲取變量
    JSValue *jsArr = ctx[@"arr"];
    
    jsArr[0] = @5;
    
    // 打印結(jié)果:5,2,3
    NSLog(@"%@",jsArr);
}

2.2 獲取定義在JS中的方法贰谣,并且調(diào)用

  • 實(shí)現(xiàn)OC調(diào)用JS方法
#pragma mark - OC調(diào)用JS
// OC調(diào)用JS方法,并獲取返回結(jié)果
- (void)ocCallJSFunc
{
    NSString *jsCode = @"function hello(say){"
    " return say; "
    "}";
    
    // 創(chuàng)建JS運(yùn)行環(huán)境
    JSContext *ctx = [[JSContext alloc] init];
    
    // 因?yàn)榉椒ㄖ苯佣x在JS中迁霎,所以可以直接通過(guò)JSContext獲取吱抚,根據(jù)方法名稱(chēng)獲取,相當(dāng)于字典的Key
    // 執(zhí)行JS代碼
    [ctx evaluateScript:jsCode];
    
    // 獲取JS方法考廉,只有先執(zhí)行JS代碼秘豹,才能獲取
    JSValue *hello = ctx[@"hello"];
    
    // OC調(diào)用JS方法,獲取方法返回值
    JSValue *result = [hello callWithArguments:@[@"你好"]];
    
    // 打印結(jié)果:你好
    NSLog(@"%@",result);
}

三昌粤、JS調(diào)用OC中的block

  • 本質(zhì):一開(kāi)始JS中并沒(méi)有OC的block既绕,所以沒(méi)法直接調(diào)用OC的block啄刹,需要把OC的block,在JS中生成方法凄贩,然后在通過(guò)JS調(diào)用鸵膏。
  • 步驟:
    • 1.創(chuàng)建JS運(yùn)行環(huán)境
    • 2.在JS中生成對(duì)應(yīng)的OC代碼
    • 3.使用JS調(diào)用,在JS環(huán)境中生成的block方法怎炊,就能調(diào)用到OC的block中.

3.1 JS調(diào)用OC中不帶參數(shù)的block

  • 想通過(guò)JS調(diào)用OC中不帶參數(shù)的block
#pragma mark - JS調(diào)用OC中不帶參數(shù)的block
- (void)jsCallOCBlock1WithNoneArguments
{
    // 創(chuàng)建JS運(yùn)行環(huán)境
    JSContext *ctx = [[JSContext alloc] init];
    
    // JS調(diào)用Block方式
    // 由于JS本身沒(méi)有OC這個(gè)代碼谭企,需要給JS中賦值,就會(huì)自動(dòng)生成右邊的代碼.
    // 相當(dāng)于在JS中定義一個(gè)叫eat的方法评肆,eat的實(shí)現(xiàn)就是block中的實(shí)現(xiàn)债查,只要調(diào)用eat,就會(huì)調(diào)用block
    ctx[@"eat"] = ^(){
        NSLog(@"吃東西");
    };
    
    // JS執(zhí)行代碼,就會(huì)直接調(diào)用到block中
    NSString *jsCode = @"eat()";
    
    [ctx evaluateScript:jsCode];
}

3.2 JS調(diào)用OC中帶參數(shù)的block

  • 想通過(guò)JS調(diào)用OC中帶參數(shù)的block
- (void)jsCallOCBlockWithArguments
{
    // 創(chuàng)建JS運(yùn)行環(huán)境
    JSContext *ctx = [[JSContext alloc] init];
    
    // 2.調(diào)用帶有參數(shù)的block
    // 還是一樣的寫(xiě)法瓜挽,會(huì)在JS中生成eat方法盹廷,只不過(guò)通過(guò)[JSContext currentArguments]獲取JS執(zhí)行方法時(shí)的參數(shù)
    ctx[@"eat"] = ^(){
        // 獲取JS調(diào)用參數(shù)
        NSArray *arguments = [JSContext currentArguments];
        NSLog(@"吃%@",arguments[0]);
    };
    
    // JS執(zhí)行代碼,調(diào)用eat方法,并傳入?yún)?shù)面包
    NSString *jsCode = @"eat('面包')";
    
    [ctx evaluateScript:jsCode];

}

四久橙、JS調(diào)用OC中的類(lèi)

  • 本質(zhì):一開(kāi)始JS中并沒(méi)有OC的類(lèi)俄占,需要先在JS中生成OC的類(lèi),然后在通過(guò)JS調(diào)用淆衷。
  • 步驟
    • 1.OC類(lèi)必須遵守JSExport協(xié)議缸榄,只要遵守JSExport協(xié)議,JS才會(huì)生成這個(gè)類(lèi)
    • 2.但是還不夠祝拯,類(lèi)里面有屬性和方法甚带,也要在JS中生成
    • 3.JSExport本身不自帶屬性和方法,需要自定義一個(gè)協(xié)議佳头,繼承JSExport鹰贵,在自己的協(xié)議中暴露需要在JS中用到的屬性和方法
    • 4.這樣自己的類(lèi)只要繼承自己的協(xié)議就好,JS就會(huì)自動(dòng)生成類(lèi)康嘉,包括自己協(xié)議中聲明的屬性和方法

4.1 JS調(diào)用OC自定義類(lèi)

  • 自定義協(xié)議(PersonJSExport)
@protocol PersonJSExport <JSExport>

@property (nonatomic, strong) NSString *name;

- (void)play;

// 調(diào)用多個(gè)參數(shù)的方法碉输,JS函數(shù)命名規(guī)則和OC還不一樣,很可能調(diào)用不到對(duì)應(yīng)的JS生成的函數(shù)亭珍,為了保證生成的JS函數(shù)和OC方法名一致敷钾,OC提供了一個(gè)宏JSExportAs,用來(lái)告訴JS應(yīng)該生成什么樣的函數(shù)對(duì)應(yīng)OC的方法块蚌,這樣就不會(huì)調(diào)錯(cuò)了闰非。

// PropertyName:JS函數(shù)生成的名字
// Selector:OC方法名
// JS就會(huì)自動(dòng)生成playGame這個(gè)方法
JSExportAs(playGame, - (void)playWithGame:(NSString *)game time:(NSString *)time);

@end

  • 自定義類(lèi)(Person)
@interface Person : NSObject <PersonJSExport>

@property (nonatomic, strong) NSString *name;


- (void)playWithGame:(NSString *)game time:(NSString *)time;

@end


@implementation Person

- (void)play
{
    
    NSLog(@"%@玩",_name);
}

- (void)playWithGame:(NSString *)game time:(NSString *)time
{
    NSLog(@"%@在%@玩%@",_name,time,game);
}


@end

  • 通過(guò)JS調(diào)用OC自定義類(lèi)


#pragma mark - JS調(diào)用OC自定義類(lèi)
- (void)jsCallOCCustomClass
{
    // 創(chuàng)建Person對(duì)象
    Person *p = [[Person alloc] init];
    p.name = @"yz";
    
    JSContext *ctx = [[JSContext alloc] init];
    
    // 會(huì)在JS中生成Person對(duì)象,并且擁有所有值
    // 前提:Person對(duì)象必須遵守JSExport協(xié)議峭范,
    ctx[@"person"] = p;
    
    // 執(zhí)行JS代碼
    // 注意:這里的person一定要跟上面聲明的一樣财松,因?yàn)樯傻膶?duì)象是用person引用
    // NSString *jsCode = @"person.play()";
    NSString *jsCode = @"person.playGame('德州撲克','晚上')";
    
    [ctx evaluateScript:jsCode];

}

4.1 JS調(diào)用OC系統(tǒng)類(lèi)

  • 問(wèn)題:系統(tǒng)自帶的類(lèi),想要通過(guò)JS調(diào)用怎么辦,我們又沒(méi)辦法修改系統(tǒng)自帶類(lèi)的文件

    • 和調(diào)用自定義類(lèi)一樣辆毡,也要弄個(gè)自定義協(xié)議繼承JSExport菜秦,描述需要暴露哪些屬性(想要把系統(tǒng)類(lèi)的哪些屬性暴露,就在自己的協(xié)議聲明)
    • 通過(guò)runtime,給類(lèi)添加協(xié)議
  • 自定義協(xié)議(UILabelJSExport)

@protocol UILabelJSExport <JSExport>

@property (nonatomic, strong) NSString *text;

@end
  • JS調(diào)用OC系統(tǒng)類(lèi)
#pragma mark - JS調(diào)用OC系統(tǒng)類(lèi)
- (void)jsCallOCSystemClass
{
    // 給系統(tǒng)類(lèi)添加協(xié)議
    class_addProtocol([UILabel class], @protocol(UILabelJSExport));
    
    // 創(chuàng)建UILabel
    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(50, 50, 100, 100)];
    [self.view addSubview:label];
    
    JSContext *ctx = [[JSContext alloc] init];
    // 就會(huì)在JS中生成label對(duì)象舶掖,并且用laebl引用
    ctx[@"label"] = label;
    
    // 利用JS給label設(shè)置文本內(nèi)容
    NSString *jsCode = @"label.text = 'Oh Year'";
    
    [ctx evaluateScript:jsCode];
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末球昨,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子眨攘,更是在濱河造成了極大的恐慌主慰,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,366評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鲫售,死亡現(xiàn)場(chǎng)離奇詭異共螺,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)情竹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)藐不,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人秦效,你說(shuō)我怎么就攤上這事雏蛮。” “怎么了阱州?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,689評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵挑秉,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我贡耽,道長(zhǎng)衷模,這世上最難降的妖魔是什么鹊汛? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,925評(píng)論 1 295
  • 正文 為了忘掉前任蒲赂,我火速辦了婚禮,結(jié)果婚禮上刁憋,老公的妹妹穿的比我還像新娘滥嘴。我一直安慰自己,他們只是感情好至耻,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布若皱。 她就那樣靜靜地躺著,像睡著了一般尘颓。 火紅的嫁衣襯著肌膚如雪走触。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,727評(píng)論 1 305
  • 那天疤苹,我揣著相機(jī)與錄音互广,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛惫皱,可吹牛的內(nèi)容都是我干的像樊。 我是一名探鬼主播,決...
    沈念sama閱讀 40,447評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼旅敷,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼生棍!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起媳谁,我...
    開(kāi)封第一講書(shū)人閱讀 39,349評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤涂滴,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后晴音,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體氢妈,經(jīng)...
    沈念sama閱讀 45,820評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評(píng)論 3 337
  • 正文 我和宋清朗相戀三年段多,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了首量。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,127評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡进苍,死狀恐怖加缘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情觉啊,我是刑警寧澤拣宏,帶...
    沈念sama閱讀 35,812評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站杠人,受9級(jí)特大地震影響勋乾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜嗡善,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評(píng)論 3 331
  • 文/蒙蒙 一辑莫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧罩引,春花似錦各吨、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,017評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至剔桨,卻和暖如春屉更,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背洒缀。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,142評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工瑰谜, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,388評(píng)論 3 373
  • 正文 我出身青樓似舵,卻偏偏與公主長(zhǎng)得像脚猾,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子砚哗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 本文由我們團(tuán)隊(duì)的 糾結(jié)倫 童鞋撰寫(xiě)龙助。 寫(xiě)在前面 本篇文章是對(duì)我一次組內(nèi)分享的整理,大部分圖片都是直接從keynot...
    知識(shí)小集閱讀 15,252評(píng)論 11 172
  • 注:本文copy自http://www.reibang.com/p/ac534f508fb0蛛芥,純屬當(dāng)筆記使用提鸟。 概...
    BookKeeping閱讀 732評(píng)論 1 3
  • 寫(xiě)在前面 本篇文章是對(duì)我一次組內(nèi)分享的整理,大部分圖片都是直接從keynote上截圖下來(lái)的仅淑,本來(lái)有很多炫酷動(dòng)效的称勋,...
    等開(kāi)會(huì)閱讀 14,457評(píng)論 6 69
  • 一. JavaScriptCore 簡(jiǎn)介 1.1 JavaScriptCore 和 JavaScriptCore ...
    GShining閱讀 859評(píng)論 0 0
  • 【五律?長(zhǎng)江神游】(平水韻) 未見(jiàn)長(zhǎng)江水,先聞四百州涯竟。 六朝煙影去赡鲜,九派浪濤留。 懷古思鄉(xiāng)國(guó)庐船,登高望客舟银酬。 故園三...
    依嵐聽(tīng)雪閱讀 481評(píng)論 4 7