react-native剖析之ios解析導(dǎo)出原生module

前言

我們知道,在ios的react-native開發(fā)中,可以通過自定義原生模塊,來實(shí)現(xiàn)js調(diào)用原生功能。

先看下步驟:
1、創(chuàng)建類TestModule實(shí)現(xiàn)協(xié)議RCTBridgeModule

2、在.m文件中新增RCT_EXPORT_MODULE()

3、如要導(dǎo)出方法給js用,那么新增:
RCT_EXPORT_METHOD(add:(int)a b:(int)b callback:(RCTResponseSenderBlock)callback){
callback(@[ [NSNumber numberWithInt: a+b ] ]);
}

大概的代碼為:
TestModule.h

#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
@interface TestModule : NSObject<RCTBridgeModule>

@end

TestModule.m


@implementation TestModule
RCT_EXPORT_MODULE()

RCT_EXPORT_METHOD(add:(int)a b:(int)b callback:(RCTResponseSenderBlock)callback){
  callback(@[  [NSNumber numberWithInt: a+b ] ]);
}

@end

這里react-native是經(jīng)過了怎樣的解析過程呢?

分析

RCT_EXPORT_MODULE做了什么

查看RCT_EXPORT_MODULE源碼:

#define RCT_EXPORT_MODULE(js_name) \
RCT_EXTERN void RCTRegisterModule(Class); \
+ (NSString *)moduleName { return @#js_name; } \
+ (void)load { RCTRegisterModule(self); }

RCTRegisterModule

void RCTRegisterModule(Class);
void RCTRegisterModule(Class moduleClass)
{
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    RCTModuleClasses = [NSMutableArray new];
  });

  RCTAssert([moduleClass conformsToProtocol:@protocol(RCTBridgeModule)],
            @"%@ does not conform to the RCTBridgeModule protocol",
            moduleClass);

  // Register module
  [RCTModuleClasses addObject:moduleClass];
}

這段代碼很好理解,將模塊的Class增加到全局?jǐn)?shù)組中

RCT_EXTERN

#if defined(__cplusplus)
#define RCT_EXTERN extern "C" __attribute__((visibility("default")))
#define RCT_EXTERN_C_BEGIN extern "C" {
#define RCT_EXTERN_C_END }
#else
#define RCT_EXTERN extern __attribute__((visibility("default")))
#define RCT_EXTERN_C_BEGIN
#define RCT_EXTERN_C_END
#endif

這段就不那么好理解了,這里涉及三個(gè)宏
1、#if defined(__cplusplus) 判斷是不是c++語言
2、attribute
參考文章:c語言中attribute的意義
3、attribute((visibility("default")))
參考這里
這里簡(jiǎn)單的理解為,外部可見,作用和原版的extern沒有什么區(qū)別。

將上面的宏定義組裝一下:
如果對(duì)宏不熟悉,可以先看看這里

extern __attribute__((visibility("default"))) void RCTRegisterModule(Class);
+ (NSString *)moduleName { return @"TestModule" ;}
+ (void)load { RCTRegisterModule(self); }

將這段代碼替換RCT_EXPORT_MODULE()

并在這兩個(gè)函數(shù)調(diào)用這里下個(gè)斷點(diǎn),在編譯運(yùn)行,馬上就看出是怎么加載的。

image.png

1、關(guān)于load函數(shù),請(qǐng)看這里細(xì)說OC中的load和initialize方法
在load里面已經(jīng)調(diào)用RCTRegisterModule注冊(cè)了本Class

2、關(guān)于如何初始化加載本模塊

image.png

從調(diào)用堆棧上可以看出一些端倪,下面一個(gè)個(gè)分析。

(1)、首先是這里初始化整個(gè)react視圖

RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                                      moduleName:@"sc"
                                               initialProperties:nil
                                                   launchOptions:launchOptions];

(2)、比較關(guān)鍵的代碼

  // Set up moduleData for automatically-exported modules
  NSArray<RCTModuleData *> *moduleDataById = [self registerModulesForClasses:modules];

 NSMutableArray<RCTModuleData *> *moduleDataByID = [NSMutableArray arrayWithCapacity:moduleClasses.count];
  for (Class moduleClass in moduleClasses) {
    NSString *moduleName = RCTBridgeModuleNameForClass(moduleClass);

    // Don't initialize the old executor in the new bridge.
    // TODO mhorowitz #10487027: after D3175632 lands, we won't need
    // this, because it won't be eagerly initialized.
    if ([moduleName isEqualToString:@"RCTJSCExecutor"]) {
      continue;
    }

    // Check for module name collisions
    RCTModuleData *moduleData = _moduleDataByName[moduleName];
    if (moduleData) {
      if (moduleData.hasInstance) {
        // Existing module was preregistered, so it takes precedence
        continue;
      } else if ([moduleClass new] == nil) {
        // The new module returned nil from init, so use the old module
        continue;
      } else if ([moduleData.moduleClass new] != nil) {
        // Both modules were non-nil, so it's unclear which should take precedence
        RCTLogError(@"Attempted to register RCTBridgeModule class %@ for the "
                    "name '%@', but name was already registered by class %@",
                    moduleClass, moduleName, moduleData.moduleClass);
      }
    }

     //這里將模塊有關(guān)的信息先封裝起來,以便以后懶加載調(diào)用
    moduleData = [[RCTModuleData alloc] initWithModuleClass:moduleClass bridge:self];

    _moduleDataByName[moduleName] = moduleData;
    [_moduleClassesByID addObject:moduleClass];
    [moduleDataByID addObject:moduleData];
  }
  [_moduleDataByID addObjectsFromArray:moduleDataByID];

  RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");

  return moduleDataByID;

RCT_EXPORT_METHOD 做了什么

先跟蹤看看源碼:

#define RCT_EXPORT_METHOD(method) \
  RCT_REMAP_METHOD(, method)
#define RCT_REMAP_METHOD(js_name, method) \
  _RCT_EXTERN_REMAP_METHOD(js_name, method, NO) \
  - (void)method;

#define _RCT_EXTERN_REMAP_METHOD(js_name, method, is_blocking_synchronous_method) \
  + (const RCTMethodInfo *)RCT_CONCAT(__rct_export__, RCT_CONCAT(js_name, RCT_CONCAT(__LINE__, __COUNTER__))) { \
    static RCTMethodInfo config = {#js_name, #method, is_blocking_synchronous_method}; \
    return &config; \
  }
#define RCT_CONCAT2(A, B) A ## B
#define RCT_CONCAT(A, B) RCT_CONCAT2(A, B)

注意這里COUNTER是計(jì)數(shù)器,每次使用都會(huì)加1,LINE為行號(hào)

這里照樣將上面的宏定義組裝一下


 + (const RCTMethodInfo *)__rct_export__行號(hào)計(jì)數(shù)器號(hào){ 
    static RCTMethodInfo config = {"", "add:(int)a b:(int)b callback:(RCTResponseSenderBlock)callback", NO}; 
    return &config; 
  }
-(void)add:(int)a b:(int)b callback:(RCTResponseSenderBlock)callback;

這里的rct_export函數(shù)的作用是利用NSStringFromSelector找到對(duì)應(yīng)的selector,以便將來可以執(zhí)行這個(gè)函數(shù)。

總結(jié)

RCT_EXPORT_MODULE 這個(gè)宏用于讓系統(tǒng)知道有這么個(gè)類,RCT_EXPORT_METHOD這個(gè)宏用于讓系統(tǒng)知道這個(gè)類有哪些方法是可以調(diào)用的,并且獲取了這個(gè)方法的selector。

最后編輯于
?著作權(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)容

  • React Native 是最近非?;鸬囊粋€(gè)話題,介紹如何利用 React Native 進(jìn)行開發(fā)的文章和書籍多如...
    零度_不結(jié)冰閱讀 782評(píng)論 0 1
  • ? React Native(以下簡(jiǎn)稱RN)的目標(biāo)是用基于react的JavaScript寫代碼,在iOS/A...
    Iceguest閱讀 3,776評(píng)論 0 10
  • 本文將講述下在原生和React Native之間的通信方式。方式和邏輯綜合了自己的思維方式,主要參考了React ...
    朱_源浩閱讀 28,621評(píng)論 25 84
  • 1. 有個(gè)同事,跟我同齡,但做起事來卻沒那么成熟,比如她最大的問題就是愛出風(fēng)頭,事來了,她會(huì)自覺把自己當(dāng)成最出臉那...
    旅小羊閱讀 1,511評(píng)論 7 2
  • 月光下依稀看到她纖瘦的手指上一個(gè)紅色的圓點(diǎn),她吐出一團(tuán)白色的煙圈,在燈光的映照下氤氳升騰。 她拉了下身上穿著的那件...
    小靈珊閱讀 396評(píng)論 1 1

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