前言
我們知道,在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)行,馬上就看出是怎么加載的。

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

從調(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。