ReactNative iOS源码解析

摘自 

折腾范儿の味精 博文。

ReactNative 概要

ReactNative,动态,跨平台,热更新,这几个词现在越来越火了,一句使用JavaScript写原生App吸引力了无数人的眼球,并且诞生了这么久也逐渐趋于稳定,携程,天猫,QZone也都在大产品线的业务上,部分模块采用这个方案上线,并且效果得到了验证(见2016 GMTC 资料PPT)

我们把这个单词拆解成2部分

React

熟悉前端的朋友们可能都知道React.JS这个前端框架,没错整个RN框架的JS代码部分,就是React.JS,所有这个框架的特点,完完全全都可以在RN里面使用(这里还融入了Flux,很好的把传统的MVC重组为dispatch,store和components,Flux架构

所以说,写RN哪不懂了,去翻React.JS的文档或许都能给你解答。

Native

顾名思义,纯原生的native体验,纯原生的UI组件,纯原生的触摸响应,纯原生的模块功能

那么这两个不相干的东西是如何关联在一起的呢?

React.JS是一个前端框架,在浏览器内H5开发上被广泛使用,他在渲染render()这个环节,在经过各种flexbox布局算法之后,要在确定的位置去绘制这个界面元素的时候,需要通过浏览器去实现。他在响应触摸touchEvent()这个环节,依然是需要浏览器去捕获用户的触摸行为,然后回调React.JS

上面提到的都是纯网页,纯H5,但如果我们把render()这个事情拦截下来,不走浏览器,而是走native会怎样呢?

当React.JS已经计算完每个页面元素的位置大小,本来要传给浏览器,让浏览器进行渲染,这时候我们不传给浏览器了,而是通过一个JS/OC的桥梁,去通过[[UIView alloc]initWithFrame:frame]的OC代码,把这个界面元素渲染了,那我们就相当于用React.JS绘制出了一个native的View

拿我们刚刚绘制出得native的View,当他发生native源生的- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event触摸事件的时候,通过一个OC/JS的桥梁,去调用React.JS里面写好的点击事件JS代码

这样React.JS还是那个React.JS,他的使用方法没发生变化,但是却获得了纯源生native的体验,native的组件渲染,native的触摸响应

于是,这个东西就叫做React-Native。

ReactNative 结构

大家可以看到,刚才我说的核心就是一个桥梁,无论是JS=>OC,还是OC=>JS。

刚才举得例子,就相当于把纯原生的UI模块,接入这个桥梁,从而让原生UI与React.JS融为一体。

那我们把野心放长远点,我们不止想让React.JS操作UI,我还想用JS操作数据库!无论是新玩意Realm,还是老玩意CoreData,FMDB,我都希望能用JS操作应该怎么办?好办,把纯原生的DB代码模块,接入这个桥梁

如果我想让JS操作Socket做长连接呢?好办,把原生socket代码模块接入这个桥梁。如果我想让JS能操作支付宝,微信,苹果IAP呢?好办,把原生支付代码模块接入这个桥梁

由此可见RN就是由一个bridge桥梁,连接起了JS与na的代码模块

链接了哪个模块,哪个模块就能用JS来操作,就能动态更新

发现现有RN框架有些功能做不到了?扩展写个native代码模块,接入这个桥梁

这是一个极度模块化可扩展的桥梁框架,不是说你从facebook的源上拉下来RN的代码,RN的能力就固定一成不变了,他的模块化可扩展,让你缺啥补上啥就好了。

ReactNative 结构图

大家可以看这个结构图,整个RN的结构分为四个部分,上面提到的,RN桥的模块化可扩展性,就体现在JSBridge/OCBridge里的ModuleConfig,只要遵循RN的协议RCTBridgeModule去写的OC Module对象,使用RCT_EXPORT_MODULE()宏注册类,使用RCT_EXPORT_METHOD()宏注册方法,那么这个OC Module以及他的OC Method都会被JS与OC的ModuleConfig进行统一控制。

上面是RN的代码类结构图

○ 大家可以看到RCTRootView是RN的根视图。

      ○ 他内部持有了一个RCTBridge,但是这个RCTBridge并没有太多的代码,而是持有了另一个           RCTBatchBridge对象,大部分的业务逻辑都转发给BatchBridge,BatchBridge里面写               着的大量的核心代码

              ○ BatchBridge会通过RCTJavaScriptLoader来加载JSBundle,在加载完毕后,这个                    loader也没什么太大的用了

              ○ BatchBridge会持有一个RCTDisplayLink,这个对象主要用于一些Timer,                              Navigator的Module需要按着屏幕渲染频率回调JS用的,只是给部分Module需求使                    用

              ○ RCTModuleXX所有的RN的Module组件都是RCTModuleData,无论是RN的核心系                  统组件,还是扩展的UI组件,API组件

                        ○ RCTJSExecutor是一个很特殊的RCTModuleData,虽然他被当做组件                                      module一起管理,统一注册,但他是系统组件的核心之一,他负责单独开一                              个线程,执行JS代码,处理JS回调,是bridge的核心通道

                        ○ RCTEventDispatcher也是一个很特殊的RCTModuleData,虽然他被当做组                            件module一起管理,统一注册,但是他负责的是各个业务模块通过他主动发                               起调用js,比如UIModule,发生了点击事件,是通过他主动回调JS的,他回调                             JS也是通过RCTJSExecutor来操作,他的作用是封装了eventDispatcher得API                            来方便业务Module使用。



ReactNative 初始化代码分析

我会按着函数调用栈类似的形式梳理出一个代码流程表,对每一个调用环节进行简单标记与作用说明,在整个表梳理完毕后,我会一一把每个标记进行详细的源码分析和解释

下面的代码流程表,如果有类名+方法的,你可以直接在RN源码中定位到具体代码段

○RCTRootView-initWithBundleURLXXX(RootInit标记)

    ○RCTBridge-initWithBundleXXX

        ○RCTBridge-createBatchedBridge(BatchBridgeInit标记

            ○New Displaylink(DisplaylinkInit标记

            ○New dispatchQueue (dispatchQueueInit标记)

            ○New dispatchGroup (dispatchGroupInit标记)

            ○group Enter(groupEnterLoadSource标记

                  ○RCTBatchedBridge-loadSource (loadJS标记)

            ○RCTBatchedBridge-initModulesWithDispatchGroup(InitModule标记这块内容非                 常多,有个子代码流程表)

            ○group Enter(groupEnterJSConfig标记

                  ○RCTBatchedBridge-setUpExecutor(configJSExecutor标记

                  ○RCTBatchedBridge-moduleConfig(moduleConfig标记

                  ○RCTBatchedBridge-injectJSONConfiguration(moduleConfigInject标记

            ○group Notify(groupDone标记

                  ○RCTBatchedBridge-executeSourceCode(evaluateJS标记

                  ○RCTDisplayLink-addToRunLoop(addrunloop标记



RootInit标记:所有RN都是通过init方法创建的不再赘述,URL可以是网络url,也可以是本地filepath转成URL

BatchBridgeInit标记:前边说过rootview会先持有一个RCTBridge,所有的module都是直接操作bridge所提供的接口,但是这个bridge基本上不干什么核心逻辑代码,他内部持有了一个batchbridge,各种调用都是直接转发给RCTBatchBridge来操作,因此batchbridge才是核心

RCTBridge在init的时候调用[self setUp]

RCTBridge在setUp的时候调用[self createBatchedBridge]

DisplaylinkInit标记:batchbridge会首先初始化一个RCTDisplayLink这个东西在业务逻辑上不会被所有的module调用,他的作用是以设备屏幕渲染的频率触发一个timer,判断是否有个别module需要按着timer去回调js,如果没有module,这个模块其实就是空跑一个displaylink,注意,此时只是初始化,并没有run这个displaylink

dispatchQueueInit标记:会初始化一个GCDqueue,后面很多操作都会被扔到这个队列里,以保证顺序执行

dispatchGroupInit标记:后面接下来进行的一些列操作,都会被添加到这个GCDgroup之中,那些被我做了group Enter标记的,当group内所有事情做完之后,会触发group Notify

groupEnterLoadSource标记:会把无论是从网络还是从本地,拉取jsbundle这个操作,放进GCDgroup之中,这样只有这个操作进行完了(还有其他group内操作执行完了,才会执行notify的任务)

loadJS标记:其实就是异步去拉取jsbundle,无论是本地读还是网络啦,[RCTJavaScriptLoader loadBundleAtURL:self.bundleURL onComplete:onSourceLoad];只有当回调完成之后会执行dispatch_group_leave,离开group

InitModule标记:这个函数是在主线程被执行的,但是刚才生成的GCD group会被当做参数传进内部,因为内部的一些逻辑是需要加入group的,这个函数内部很复杂 我会继续绘制一个代码流程表

1)RCTGetModuleClasses()

一个C函数,RCT_EXPORT_MODULE()注册宏会在+load时候把Module类都统一管理在一个static NSArray里,通过RCTGetModuleClasses()可以取出来所有的Module

2)RCTModuleData-initWithModuleClass

此处是一个for循环,循环刚才拿到的array,对每一个注册了得module都循环生成RCTModuleData实例

3)配置moduleConfig

每一个module在循环生成结束后,bridge会统一存储3分配置表,包含了所有的moduleConfig的信息,便于查找和管理.

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

推荐阅读更多精彩内容