ReactNative iOS源碼解析

摘自?

折騰范兒の味精 博文。

ReactNative 概要

ReactNative览濒,動態(tài)次和,跨平臺反肋,熱更新,這幾個(gè)詞現(xiàn)在越來越火了踏施,一句使用JavaScript寫原生App吸引力了無數(shù)人的眼球石蔗,并且誕生了這么久也逐漸趨于穩(wěn)定,攜程,天貓,QZone也都在大產(chǎn)品線的業(yè)務(wù)上畅形,部分模塊采用這個(gè)方案上線养距,并且效果得到了驗(yàn)證(見2016 GMTC 資料PPT)

我們把這個(gè)單詞拆解成2部分

React

熟悉前端的朋友們可能都知道React.JS這個(gè)前端框架日熬,沒錯(cuò)整個(gè)RN框架的JS代碼部分棍厌,就是React.JS,所有這個(gè)框架的特點(diǎn),完完全全都可以在RN里面使用(這里還融入了Flux耘纱,很好的把傳統(tǒng)的MVC重組為dispatch敬肚,store和components,Flux架構(gòu)

所以說束析,寫RN哪不懂了帘皿,去翻React.JS的文檔或許都能給你解答。

Native

顧名思義畸陡,純原生的native體驗(yàn)鹰溜,純原生的UI組件,純原生的觸摸響應(yīng)丁恭,純原生的模塊功能

那么這兩個(gè)不相干的東西是如何關(guān)聯(lián)在一起的呢曹动?

React.JS是一個(gè)前端框架,在瀏覽器內(nèi)H5開發(fā)上被廣泛使用牲览,他在渲染render()這個(gè)環(huán)節(jié)墓陈,在經(jīng)過各種flexbox布局算法之后,要在確定的位置去繪制這個(gè)界面元素的時(shí)候第献,需要通過瀏覽器去實(shí)現(xiàn)贡必。他在響應(yīng)觸摸touchEvent()這個(gè)環(huán)節(jié),依然是需要瀏覽器去捕獲用戶的觸摸行為庸毫,然后回調(diào)React.JS

上面提到的都是純網(wǎng)頁仔拟,純H5,但如果我們把render()這個(gè)事情攔截下來飒赃,不走瀏覽器利花,而是走native會怎樣呢?

當(dāng)React.JS已經(jīng)計(jì)算完每個(gè)頁面元素的位置大小载佳,本來要傳給瀏覽器炒事,讓瀏覽器進(jìn)行渲染,這時(shí)候我們不傳給瀏覽器了蔫慧,而是通過一個(gè)JS/OC的橋梁挠乳,去通過[[UIView alloc]initWithFrame:frame]的OC代碼,把這個(gè)界面元素渲染了姑躲,那我們就相當(dāng)于用React.JS繪制出了一個(gè)native的View

拿我們剛剛繪制出得native的View睡扬,當(dāng)他發(fā)生native源生的- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event觸摸事件的時(shí)候,通過一個(gè)OC/JS的橋梁肋联,去調(diào)用React.JS里面寫好的點(diǎn)擊事件JS代碼

這樣React.JS還是那個(gè)React.JS威蕉,他的使用方法沒發(fā)生變化,但是卻獲得了純源生native的體驗(yàn)橄仍,native的組件渲染,native的觸摸響應(yīng)

于是,這個(gè)東西就叫做React-Native侮繁。

ReactNative 結(jié)構(gòu)

大家可以看到虑粥,剛才我說的核心就是一個(gè)橋梁,無論是JS=>OC宪哩,還是OC=>JS娩贷。

剛才舉得例子,就相當(dāng)于把純原生的UI模塊锁孟,接入這個(gè)橋梁彬祖,從而讓原生UI與React.JS融為一體。

那我們把野心放長遠(yuǎn)點(diǎn)品抽,我們不止想讓React.JS操作UI储笑,我還想用JS操作數(shù)據(jù)庫!無論是新玩意Realm圆恤,還是老玩意CoreData突倍,F(xiàn)MDB,我都希望能用JS操作應(yīng)該怎么辦盆昙?好辦羽历,把純原生的DB代碼模塊,接入這個(gè)橋梁

如果我想讓JS操作Socket做長連接呢淡喜?好辦秕磷,把原生socket代碼模塊接入這個(gè)橋梁。如果我想讓JS能操作支付寶炼团,微信跳夭,蘋果IAP呢?好辦们镜,把原生支付代碼模塊接入這個(gè)橋梁

由此可見RN就是由一個(gè)bridge橋梁币叹,連接起了JS與na的代碼模塊

鏈接了哪個(gè)模塊,哪個(gè)模塊就能用JS來操作模狭,就能動態(tài)更新

發(fā)現(xiàn)現(xiàn)有RN框架有些功能做不到了颈抚?擴(kuò)展寫個(gè)native代碼模塊,接入這個(gè)橋梁

這是一個(gè)極度模塊化可擴(kuò)展的橋梁框架嚼鹉,不是說你從facebook的源上拉下來RN的代碼贩汉,RN的能力就固定一成不變了,他的模塊化可擴(kuò)展锚赤,讓你缺啥補(bǔ)上啥就好了匹舞。

ReactNative 結(jié)構(gòu)圖

大家可以看這個(gè)結(jié)構(gòu)圖,整個(gè)RN的結(jié)構(gòu)分為四個(gè)部分线脚,上面提到的赐稽,RN橋的模塊化可擴(kuò)展性叫榕,就體現(xiàn)在JSBridge/OCBridge里的ModuleConfig,只要遵循RN的協(xié)議RCTBridgeModule去寫的OC Module對象姊舵,使用RCT_EXPORT_MODULE()宏注冊類晰绎,使用RCT_EXPORT_METHOD()宏注冊方法,那么這個(gè)OC Module以及他的OC Method都會被JS與OC的ModuleConfig進(jìn)行統(tǒng)一控制括丁。

上面是RN的代碼類結(jié)構(gòu)圖

○ 大家可以看到RCTRootView是RN的根視圖荞下。

? ? ? ○ 他內(nèi)部持有了一個(gè)RCTBridge,但是這個(gè)RCTBridge并沒有太多的代碼,而是持有了另一個(gè) ? ? ? ? ??RCTBatchBridge對象史飞,大部分的業(yè)務(wù)邏輯都轉(zhuǎn)發(fā)給BatchBridge尖昏,BatchBridge里面寫 ? ? ? ? ? ? ? 著的大量的核心代碼

? ? ? ? ? ? ? ○ BatchBridge會通過RCTJavaScriptLoader來加載JSBundle,在加載完畢后构资,這個(gè) ? ? ? ? ? ? ? ? ? ?loader也沒什么太大的用了

? ? ? ? ? ? ? ○ BatchBridge會持有一個(gè)RCTDisplayLink抽诉,這個(gè)對象主要用于一些Timer, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Navigator的Module需要按著屏幕渲染頻率回調(diào)JS用的蚯窥,只是給部分Module需求使 ? ? ? ? ? ? ? ? ? ?用

? ? ? ? ? ? ? ○ RCTModuleXX所有的RN的Module組件都是RCTModuleData掸鹅,無論是RN的核心系 ? ? ? ? ? ? ? ? ?統(tǒng)組件,還是擴(kuò)展的UI組件拦赠,API組件

? ? ? ? ? ? ? ? ? ? ? ? ○ RCTJSExecutor是一個(gè)很特殊的RCTModuleData巍沙,雖然他被當(dāng)做組件 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?module一起管理,統(tǒng)一注冊荷鼠,但他是系統(tǒng)組件的核心之一句携,他負(fù)責(zé)單獨(dú)開一 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?個(gè)線程,執(zhí)行JS代碼允乐,處理JS回調(diào)矮嫉,是bridge的核心通道

? ? ? ? ? ? ? ? ? ? ? ? ○ RCTEventDispatcher也是一個(gè)很特殊的RCTModuleData,雖然他被當(dāng)做組 ? ? ? ? ? ? ? ? ? ? ? ? ? ?件module一起管理牍疏,統(tǒng)一注冊蠢笋,但是他負(fù)責(zé)的是各個(gè)業(yè)務(wù)模塊通過他主動發(fā) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 起調(diào)用js,比如UIModule鳞陨,發(fā)生了點(diǎn)擊事件昨寞,是通過他主動回調(diào)JS的,他回調(diào) ? ? ? ? ? ? ? ? ? ? ? ? ? ? JS也是通過RCTJSExecutor來操作厦滤,他的作用是封裝了eventDispatcher得API ? ? ? ? ? ? ? ? ? ? ? ? ? ?來方便業(yè)務(wù)Module使用援岩。



ReactNative 初始化代碼分析

我會按著函數(shù)調(diào)用棧類似的形式梳理出一個(gè)代碼流程表,對每一個(gè)調(diào)用環(huán)節(jié)進(jìn)行簡單標(biāo)記與作用說明掏导,在整個(gè)表梳理完畢后享怀,我會一一把每個(gè)標(biāo)記進(jìn)行詳細(xì)的源碼分析和解釋

下面的代碼流程表,如果有類名+方法的趟咆,你可以直接在RN源碼中定位到具體代碼段

○RCTRootView-initWithBundleURLXXX(RootInit標(biāo)記)

? ? ○RCTBridge-initWithBundleXXX

? ? ? ? ○RCTBridge-createBatchedBridge(BatchBridgeInit標(biāo)記

? ? ? ? ? ? ○New Displaylink(DisplaylinkInit標(biāo)記

? ? ? ? ? ? ○New dispatchQueue (dispatchQueueInit標(biāo)記)

? ? ? ? ? ? ○New dispatchGroup (dispatchGroupInit標(biāo)記)

? ? ? ? ? ? ○group Enter(groupEnterLoadSource標(biāo)記

? ? ? ? ? ? ? ? ? ○RCTBatchedBridge-loadSource (loadJS標(biāo)記)

? ? ? ? ? ? ○RCTBatchedBridge-initModulesWithDispatchGroup(InitModule標(biāo)記這塊內(nèi)容非 ? ? ? ? ? ? ? ? 常多添瓷,有個(gè)子代碼流程表)

? ? ? ? ? ? ○group Enter(groupEnterJSConfig標(biāo)記

? ? ? ? ? ? ? ? ? ○RCTBatchedBridge-setUpExecutor(configJSExecutor標(biāo)記

? ? ? ? ? ? ? ? ? ○RCTBatchedBridge-moduleConfig(moduleConfig標(biāo)記

? ? ? ? ? ? ? ? ? ○RCTBatchedBridge-injectJSONConfiguration(moduleConfigInject標(biāo)記

? ? ? ? ? ? ○group Notify(groupDone標(biāo)記

? ? ? ? ? ? ? ? ? ○RCTBatchedBridge-executeSourceCode(evaluateJS標(biāo)記

? ? ? ? ? ? ? ? ? ○RCTDisplayLink-addToRunLoop(addrunloop標(biāo)記



RootInit標(biāo)記:所有RN都是通過init方法創(chuàng)建的不再贅述梅屉,URL可以是網(wǎng)絡(luò)url,也可以是本地filepath轉(zhuǎn)成URL

BatchBridgeInit標(biāo)記:前邊說過rootview會先持有一個(gè)RCTBridge仰坦,所有的module都是直接操作bridge所提供的接口履植,但是這個(gè)bridge基本上不干什么核心邏輯代碼计雌,他內(nèi)部持有了一個(gè)batchbridge悄晃,各種調(diào)用都是直接轉(zhuǎn)發(fā)給RCTBatchBridge來操作,因此batchbridge才是核心

RCTBridge在init的時(shí)候調(diào)用[self setUp]

RCTBridge在setUp的時(shí)候調(diào)用[self createBatchedBridge]

DisplaylinkInit標(biāo)記:batchbridge會首先初始化一個(gè)RCTDisplayLink這個(gè)東西在業(yè)務(wù)邏輯上不會被所有的module調(diào)用凿滤,他的作用是以設(shè)備屏幕渲染的頻率觸發(fā)一個(gè)timer妈橄,判斷是否有個(gè)別module需要按著timer去回調(diào)js,如果沒有module翁脆,這個(gè)模塊其實(shí)就是空跑一個(gè)displaylink眷蚓,注意,此時(shí)只是初始化反番,并沒有run這個(gè)displaylink

dispatchQueueInit標(biāo)記:會初始化一個(gè)GCDqueue沙热,后面很多操作都會被扔到這個(gè)隊(duì)列里,以保證順序執(zhí)行

dispatchGroupInit標(biāo)記:后面接下來進(jìn)行的一些列操作罢缸,都會被添加到這個(gè)GCDgroup之中篙贸,那些被我做了group Enter標(biāo)記的,當(dāng)group內(nèi)所有事情做完之后枫疆,會觸發(fā)group Notify

groupEnterLoadSource標(biāo)記:會把無論是從網(wǎng)絡(luò)還是從本地爵川,拉取jsbundle這個(gè)操作,放進(jìn)GCDgroup之中息楔,這樣只有這個(gè)操作進(jìn)行完了(還有其他group內(nèi)操作執(zhí)行完了寝贡,才會執(zhí)行notify的任務(wù))

loadJS標(biāo)記:其實(shí)就是異步去拉取jsbundle,無論是本地讀還是網(wǎng)絡(luò)啦值依,[RCTJavaScriptLoader loadBundleAtURL:self.bundleURL onComplete:onSourceLoad];只有當(dāng)回調(diào)完成之后會執(zhí)行dispatch_group_leave圃泡,離開group

InitModule標(biāo)記:這個(gè)函數(shù)是在主線程被執(zhí)行的,但是剛才生成的GCD group會被當(dāng)做參數(shù)傳進(jìn)內(nèi)部愿险,因?yàn)閮?nèi)部的一些邏輯是需要加入group的颇蜡,這個(gè)函數(shù)內(nèi)部很復(fù)雜 我會繼續(xù)繪制一個(gè)代碼流程表

1)RCTGetModuleClasses()

一個(gè)C函數(shù),RCT_EXPORT_MODULE()注冊宏會在+load時(shí)候把Module類都統(tǒng)一管理在一個(gè)static NSArray里拯啦,通過RCTGetModuleClasses()可以取出來所有的Module

2)RCTModuleData-initWithModuleClass

此處是一個(gè)for循環(huán)澡匪,循環(huán)剛才拿到的array,對每一個(gè)注冊了得module都循環(huán)生成RCTModuleData實(shí)例

3)配置moduleConfig

每一個(gè)module在循環(huán)生成結(jié)束后褒链,bridge會統(tǒng)一存儲3分配置表唁情,包含了所有的moduleConfig的信息,便于查找和管理.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末甫匹,一起剝皮案震驚了整個(gè)濱河市甸鸟,隨后出現(xiàn)的幾起案子惦费,更是在濱河造成了極大的恐慌,老刑警劉巖抢韭,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件薪贫,死亡現(xiàn)場離奇詭異,居然都是意外死亡刻恭,警方通過查閱死者的電腦和手機(jī)瞧省,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鳍贾,“玉大人鞍匾,你說我怎么就攤上這事∑锟疲” “怎么了橡淑?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長咆爽。 經(jīng)常有香客問我梁棠,道長,這世上最難降的妖魔是什么斗埂? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任符糊,我火速辦了婚禮,結(jié)果婚禮上蜜笤,老公的妹妹穿的比我還像新娘濒蒋。我一直安慰自己,他們只是感情好把兔,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布沪伙。 她就那樣靜靜地躺著,像睡著了一般县好。 火紅的嫁衣襯著肌膚如雪围橡。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天缕贡,我揣著相機(jī)與錄音翁授,去河邊找鬼。 笑死晾咪,一個(gè)胖子當(dāng)著我的面吹牛收擦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播谍倦,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼塞赂,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了昼蛀?” 一聲冷哼從身側(cè)響起宴猾,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤圆存,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后仇哆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體沦辙,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年讹剔,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了油讯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,981評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡辟拷,死狀恐怖撞羽,靈堂內(nèi)的尸體忽然破棺而出阐斜,到底是詐尸還是另有隱情衫冻,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布谒出,位于F島的核電站隅俘,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏笤喳。R本人自食惡果不足惜为居,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望杀狡。 院中可真熱鬧蒙畴,春花似錦、人聲如沸呜象。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽恭陡。三九已至蹬音,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間休玩,已是汗流浹背著淆。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拴疤,地道東北人永部。 一個(gè)月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像呐矾,于是被迫代替她去往敵國和親苔埋。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評論 2 355

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