整個 Weex 的工作原理大致可以用一張圖來表述:
這次分享的重點在 Native Module 如何注冊并切可以被 JS 調(diào)用的倡鲸。
初始化
首先我們通過 [WXSDKEngine initSDKEnvironment]
初始化 SDK 環(huán)境
+ (void)initSDKEnvironment
{
……
// 獲取 main.js 文件路徑
NSString *filePath = [[NSBundle bundleForClass:self] pathForResource:@"main" ofType:@"js"];
// 讀出 main.js 文件內(nèi)容
NSString *script = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
[WXSDKEngine initSDKEnvironment:script];
……
}
+ (void)initSDKEnvironment:(NSString *)script
{
……
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 注冊默認內(nèi)容 這個稍后會說
[self registerDefaults];
// BridgeManager 執(zhí)行 JS
[[WXSDKManager bridgeMgr] executeJsFramework:script];
});
}
讓我們忽略掉一些邊界處理砾层、監(jiān)控缩滨、日志的代碼,只提取其中的核心代碼(以下同)泊业。Weex 將 Bundle 中的 main.js 加載油讯,交給 Bridge Manager 執(zhí)行。我們可以查看 main.js 是一個壓縮后的 js 文件田弥,其實它就是 Weex JS 的 Framework涛酗,我們調(diào)用的 Weex JS API 就是在這里定義的。那么剛剛執(zhí)行它的 Bridge Manager 又是做什么的呢偷厦?
Bridge 模塊
Bridge 模塊就是負責 Native 與 JS 通訊的煤杀,有下面這些類組成。
WXBridgeManager
Bridge 模塊還有一個非常重要的類沪哺,是在 Manager 路徑下的 WXBridgeManager沈自,它是整個模塊的對外API,單例辜妓。它維護了一個線程來 Loop 處理事件枯途,還關(guān)聯(lián)了 Bridge Context,比如 register module籍滴、execute JS 等 JS 的交互操作實際都是在 Bridge Context 中執(zhí)行的酪夷。下面來介紹這個 Bridge Context。
WXBridgeContext
Bridge Context 的主要功能如上文說孽惰,是給 Bridge Manager 提供與 JS 交互的能力晚岭,所以除了注冊 Module、Service 等之外勋功,就是調(diào)用 JS 方法和服務(wù)坦报,所以它維護了 sendQueue、serviceQueue 兩個隊列來分別管理方法和服務(wù)的調(diào)用消息狂鞋,另外還有一個 methodQueue片择,是來存儲在 Framework 加載之前就被發(fā)送的消息,在 Framework 加載完成之后再執(zhí)行骚揍。它還維持了一個 weex instance id stack字管,在 WXSDKManager 中有一個 id -> WXInstance 的哈希表,可以通過 id 獲取到對應(yīng)的 WXInstance信不。
WXBridgeProtocol
WXBridgeContext 真正的 JS Native 交互能力依賴于它關(guān)聯(lián)的 id<WXBridgeProtocol>嘲叔,這個 Protocol 的聲明如下:
@protocol WXBridgeProtocol <NSObject>
@property (nonatomic, readonly) JSValue* exception;
/**
* Executes the js framework code in javascript engine
* You can do some setup in this method
*/
- (void)executeJSFramework:(NSString *)frameworkScript;
/**
* Executes the js code in javascript engine
* You can do some setup in this method
*/
- (void)executeJavascript:(NSString *)script;
/**
* Executes global js method with specific arguments
*/
- (JSValue *)callJSMethod:(NSString *)method args:(NSArray*)args;
/**
* Register callback when call native tasks occur
*/
- (void)registerCallNative:(WXJSCallNative)callNative;
/**
* Reset js engine environment, called when any environment variable is changed.
*/
- (void)resetEnvironment;
@optional
/**
* Called when garbage collection is wanted by sdk.
*/
- (void)garbageCollect;
/**
* Register callback when addElement tasks occur
*/
- (void)registerCallAddElement:(WXJSCallAddElement)callAddElement;
/**
* Register callback for global js function `callNativeModule`
*/
- (void)registerCallNativeModule:(WXJSCallNativeModule)callNativeModuleBlock;
/**
* Register callback for global js function `callNativeComponent`
*/
- (void)registerCallNativeComponent:(WXJSCallNativeComponent)callNativeComponentBlock;
@end
很顯然,它提供了 Native 和 JS 交互的最基本功能抽活,包括執(zhí)行 JS 代碼硫戈,調(diào)用方法,注冊 Native 回調(diào)等酌壕。這個協(xié)議有兩個實現(xiàn)類:WXJSCoreBridge 和 WXDebugLoggerBridge掏愁。其中 WXJSCoreBridge 是使用蘋果的 JavaScriptCore 這個引擎來實現(xiàn)的。而 WXDebugLoggerBridge 則是用于調(diào)試卵牍,將 JS Bridge 對接到一個遠程的 websocket 服務(wù)器果港,而不是本地的 JS 引擎。本地準備一個網(wǎng)頁糊昙,運行了完整的 JS 引擎的代碼辛掠,并且也可以鏈接到一個遠程的 websocket 服務(wù)器,這樣客戶端的 native 層和本地網(wǎng)頁里面的 JS 引擎就串聯(lián)起來了释牺,原本通過 JS Bridge 的雙向通信內(nèi)容就可以被 websocket 連接記錄下來萝衩,JS 引擎中里的所有代碼都可以通過本地瀏覽器的開發(fā)者工具進行 debug 和 console 控制。
其他
WXBridgeMethod 是其他各種 Method 的基類没咙,他們是封裝的方法模型猩谊,包括調(diào)用對象的 Ref、方法名祭刚、參數(shù)數(shù)組牌捷、weex instance 等,可以通過獲取 NSInvocation 調(diào)用相應(yīng)的 Native 方法涡驮。
JSValue Category 是個工具暗甥,可以將 NSInvocation 的返回值轉(zhuǎn)換成 JS 對應(yīng)的類型。
WXPolyfillSet 是一個 JSExport捉捅,封裝了一個 NSMutableSet撤防,供 JS 調(diào)用。
總結(jié)
整個 Weex Bridge 模塊的主要類圖如下所示棒口。
Native Module 的注冊和調(diào)用
以 Native Module 為例寄月,學習一下 Weex 是如何通過 JS 調(diào)用 Native 代碼的。
注冊
我們通過[WXSDKEngin registerModule:withClass:]
方法來在 Weex 中注冊 Module无牵。
+ (void)registerModule:(NSString *)name withClass:(Class)clazz
{
NSString *moduleName = [WXModuleFactory registerModule:name withClass:clazz];
NSDictionary *dict = [WXModuleFactory moduleMethodMapsWithName:moduleName];
[[WXSDKManager bridgeMgr] registerModules:dict];
}
代碼中先在 ModuleFactory 中注冊 Module 并獲取 moduleName剥懒,再從在 ModuleFactory 中獲取到 moduleMethod 的哈希表,并將這個哈希表注冊在 Bridge Manager 中合敦。
下面來具體看看每一步注冊具體是如何做的初橘。
WXModuleFactory 中的注冊
- (NSString *)_registerModule:(NSString *)name withClass:(Class)clazz
{
[_moduleLock lock];
//allow to register module with the same name;
WXModuleConfig *config = [[WXModuleConfig alloc] init];
config.name = name;
config.clazz = NSStringFromClass(clazz);
[config registerMethods];
[_moduleMap setValue:config forKey:name];
[_moduleLock unlock];
return name;
}
在 ModuleFactory 的 moduleMap 中,加入了一個 WXModuleConfig充岛,WXModuleConfig 記錄了 name 和 class保檐,調(diào)用 [WXModuleConfig registerMethods]
注冊了方法。
- (void)registerMethods
{
Class currentClass = NSClassFromString(_clazz);
if (!currentClass) {
WXLogWarning(@"The module class [%@] doesn't exit崔梗!", _clazz);
return;
}
while (currentClass != [NSObject class]) {
unsigned int methodCount = 0;
Method *methodList = class_copyMethodList(object_getClass(currentClass), &methodCount);
for (unsigned int i = 0; i < methodCount; i++) {
NSString *selStr = [NSString stringWithCString:sel_getName(method_getName(methodList[i])) encoding:NSUTF8StringEncoding];
BOOL isSyncMethod = NO;
if ([selStr hasPrefix:@"wx_export_method_sync_"]) {
isSyncMethod = YES;
} else if ([selStr hasPrefix:@"wx_export_method_"]) {
isSyncMethod = NO;
} else {
continue;
}
NSString *name = nil, *method = nil;
SEL selector = NSSelectorFromString(selStr);
if ([currentClass respondsToSelector:selector]) {
method = ((NSString* (*)(id, SEL))[currentClass methodForSelector:selector])(currentClass, selector);
}
if (method.length <= 0) {
WXLogWarning(@"The module class [%@] doesn't has any method夜只!", _clazz);
continue;
}
NSRange range = [method rangeOfString:@":"];
if (range.location != NSNotFound) {
name = [method substringToIndex:range.location];
} else {
name = method;
}
NSMutableDictionary *methods = isSyncMethod ? _syncMethods : _asyncMethods;
[methods setObject:method forKey:name];
}
free(methodList);
currentClass = class_getSuperclass(currentClass);
}
}
[WXModuleConfig registerMethods]
方法非常長,簡單來說就是:先通過 while 循環(huán)遍歷自己和父類的方法列表中的每一個方法蒜魄,如果方法名以 wx_export_method_sync_ 或 wx_export_method_ 開頭扔亥,則通過 OC runtime 獲取到方法所對應(yīng)的函數(shù)指針场躯,并調(diào)用這個函數(shù),得到 method 字符串旅挤,最后將 method 字符串放入同步或異步的方法表里踢关。
這里面有幾個疑問:wx_export_method_sync、wx_export_method 開頭的方法是在哪里聲明的粘茄?調(diào)用它得到的 method 字符串又是什么签舞?
在 Weex Module 中,我們需要用 WX_EXPORT_METHOD柒瓣、WX_EXPORT_METHOD_SYNC 這兩個宏來聲名 JS 異步和同步方法儒搭,問題應(yīng)該就是在這里,所以看看這兩個宏的定義芙贫。
/*
* Concatenate preprocessor tokens a and b without expanding macro definitions
* (however, if invoked from a macro, macro arguments are expanded).
*/
#define WX_CONCAT(a, b) a ## b
/*
* Concatenate preprocessor tokens a and b after macro-expanding them.
*/
#define WX_CONCAT_WRAPPER(a, b) WX_CONCAT(a, b)
#define WX_EXPORT_METHOD_INTERNAL(method, token) \
+ (NSString *)WX_CONCAT_WRAPPER(token, __LINE__) { \
return NSStringFromSelector(method); \
}
/**
* @abstract export public method
*/
#define WX_EXPORT_METHOD(method) WX_EXPORT_METHOD_INTERNAL(method,wx_export_method_)
/**
* @abstract export public method, support sync return value
* @warning the method can only be called on js thread
*/
#define WX_EXPORT_METHOD_SYNC(method) WX_EXPORT_METHOD_INTERNAL(method,wx_export_method_sync_)
這兩個宏的作用就是定義了一個類方法搂鲫,以 wx_export_method、wx_export_method_sync_ 加上所在代碼的行數(shù)組成方法名磺平,返回 selector 的字符串默穴,上面的疑問得以解決。
回到剛才的步驟褪秀, 從 ModuleFactory 中獲取到 moduleMethod 的哈希表蓄诽,它是將 config 中的同步和異步方法都裝入一個數(shù)組中,以 moduleName 為 key媒吗,數(shù)組為 value 生成一個單項哈希表仑氛。
至此,在 WXModuleFactory 中的注冊已經(jīng)基本完成了:moduleMap 哈希表中用 moduleName 作為 key闸英,moduleConfig 作為 value锯岖;moduleConfig 中記錄著 className 和 moduleName,同時還有兩個哈希表甫何,存儲同步和異步方法出吹。
WXBridgeManager 中的注冊
Bridge Manager 是調(diào)用 Bridge Context 執(zhí)行的注冊。
- (void)registerModules:(NSDictionary *)modules
{
WXAssertBridgeThread();
if(!modules) return;
[self callJSMethod:@"registerModules" args:@[modules]];
}
- (void)callJSMethod:(NSString *)method args:(NSArray *)args
{
if (self.frameworkLoadFinished) {
[self.jsBridge callJSMethod:method args:args];
} else {
[_methodQueue addObject:@{@"method":method, @"args":args}];
}
}
它是調(diào)用了 JSBridge 的 callJSMethod 方法辙喂,method 為 "registerModules"捶牢,args 參數(shù)是這個 Module 提供給 JS 調(diào)用的所有的同步、異步方法列表巍耗。
之前介紹了 Bridge 模塊下的 JSBridgeProtocol 的兩個實現(xiàn)類秋麸,那么以 WXJSCoreBridge 為例,callJSMethod 的方法實現(xiàn)如下:
- (JSValue *)callJSMethod:(NSString *)method args:(NSArray *)args
{
return [[_jsContext globalObject] invokeMethod:method withArguments:args];
}
直接調(diào)用了 JS 全局對象的registerModules
方法炬太。那么我們就看看 JS 中的 registerModules 方法是如何定義的灸蟆。
/**
* Register the name and methods of each module.
* @param {object} modules a object of modules
*/
export function registerModules (modules) {
/* istanbul ignore else */
if (typeof modules === 'object') {
initModules(modules)
}
}
/**
* init modules for an app instance
* the second param determines whether to replace an existed method
*/
export function initModules (modules, ifReplace) {
for (const moduleName in modules) {
// init `modules[moduleName][]`
let methods = nativeModules[moduleName]
if (!methods) {
methods = {}
nativeModules[moduleName] = methods
}
// push each non-existed new method
modules[moduleName].forEach(function (method) {
if (typeof method === 'string') {
method = {
name: method
}
}
if (!methods[method.name] || ifReplace) {
methods[method.name] = method
}
})
}
}
簡而言之就是將 name 和方法哈希中的方法都寫入了 nativeModules 這個對象中。
調(diào)用
我們在 JS 中使用weex.requireModule('xxx')
來獲取 module亲族,再調(diào)用它的方法炒考。那么 weex 這個全局變量是哪里定義的呢可缚?它是怎么能夠被獲取到的?requireModule()
又做了什么斋枢?
weex 對象
每個 weex 頁面都是一個 WXSDKInstance帘靡,weex instance 在加載到 JS bundle 的代碼之后,會調(diào)用renderWithMainBundleString:
方法杏慰。
- (void)_renderWithMainBundleString:(NSString *)mainBundleString
{
NSMutableDictionary *dictionary = [_options mutableCopy];
WXPerformBlockOnMainThread(^{
_rootView = [[WXRootView alloc] initWithFrame:self.frame];
_rootView.instance = self;
if(self.onCreate) {
self.onCreate(_rootView);
}
});
// ensure default modules/components/handlers are ready before create instance
[WXSDKEngine registerDefaults];
[[WXSDKManager bridgeMgr] createInstance:self.instanceId template:mainBundleString options:dictionary data:_jsData];
}
先在主線程中創(chuàng)建了 WXRootView空另,并調(diào)用了 onCreate 回調(diào)黄鳍,最后調(diào)用了[WXBridgeManager createInstance:template:options:data]
旗笔,將 mainBundleString 傳給了 BridgeManager朽肥。
在 BridgeManager 中切換了執(zhí)行線程手蝎,將任務(wù)交給 Bridge Context 來執(zhí)行活喊。
- (void)createInstance:(NSString *)instance
template:(NSString *)temp
options:(NSDictionary *)options
data:(id)data
{
……
[self callJSMethod:@"createInstance" args:args];
}
最后 Bridge Context 調(diào)用了createInstance()
這個 JS 方法搜囱,將 instanceId以舒、mainBundleString 等參數(shù)都傳了過去霎肯。
我們看看 JS 中createInstance()
這個方法的實現(xiàn)擎颖。
/*
* @param {string} id
* @param {string} code
* @param {object} options
* option `HAS_LOG` enable print log
* @param {object} data
* @param {object} info { created, ... services }
*/
export function createInstance (id, code, options, data, info) {
const { services } = info || {}
resetTarget()
let instance = instanceMap[id]
/* istanbul ignore else */
options = options || {}
let result
/* istanbul ignore else */
if (!instance) {
instance = new App(id, options)
instanceMap[id] = instance
result = initApp(instance, code, data, services)
}
else {
result = new Error(`invalid instance id "${id}"`)
}
return result
}
export function init (app, code, data, services) {
//……
//……
/* istanbul ignore next */
const bundleRequireModule = name => app.requireModule(removeWeexPrefix(name))
const weexGlobalObject = {
config: app.options,
define: bundleDefine,
bootstrap: bundleBootstrap,
requireModule: bundleRequireModule,
document: bundleDocument,
Vm: bundleVm
}
Object.freeze(weexGlobalObject)
// prepare code
let functionBody
/* istanbul ignore if */
if (typeof code === 'function') {
// `function () {...}` -> `{...}`
// not very strict
functionBody = code.toString().substr(12)
}
/* istanbul ignore next */
else if (code) {
functionBody = code.toString()
}
// wrap IFFE and use strict mode
functionBody = `(function(global){\n\n"use strict";\n\n ${functionBody} \n\n})(Object.create(this))`
// ……
// run code and get result
const globalObjects = Object.assign({
define: bundleDefine,
require: bundleRequire,
bootstrap: bundleBootstrap,
register: bundleRegister,
render: bundleRender,
__weex_define__: bundleDefine, // alias for define
__weex_bootstrap__: bundleBootstrap, // alias for bootstrap
__weex_document__: bundleDocument,
__weex_require__: bundleRequireModule,
__weex_viewmodel__: bundleVm,
weex: weexGlobalObject
}, timerAPIs, services)
callFunction(globalObjects, functionBody)
return result
}
/**
* Call a new function body with some global objects.
* @param {object} globalObjects
* @param {string} code
* @return {any}
*/
function callFunction (globalObjects, body) {
const globalKeys = []
const globalValues = []
for (const key in globalObjects) {
globalKeys.push(key)
globalValues.push(globalObjects[key])
}
globalKeys.push(body)
const result = new Function(...globalKeys)
return result(...globalValues)
}
代碼非常多,所以只截取了我們今天要關(guān)注的部分观游。
首先在createInstance()
函數(shù)中搂捧,創(chuàng)建了 App 對象,并將它存儲在 instanceMap 中懂缕。
在init()
函數(shù)中允跑,先定義了 weexGlobalObject,這也就是我們 JS bundle 代碼中要使用的 weex 對象搪柑,其中也聲名了我們需要的requireModule()
方法聋丝。而后又將傳入的 mainBundleString 封裝在了 IIFE 中。然后又定義了 globalObjects 對象工碾,里面除了包含了 weex弱睦、require、bootstrap渊额、render 等多個成員外况木,也包含了 timerAPIs 和 services 中的成員。
最后在callFunction()
函數(shù)中旬迹,將 globalObjects 中的所有成員都作為參數(shù)焦读,調(diào)用了之前封裝的 IIFE。至此我們的 JS bundle 代碼中直接使用的 weex 實例就被這樣傳遞過來了舱权。
requireModule()
后面的問題就簡單多了矗晃,在 App 中查看requireModule()
的實現(xiàn)。
/**
* @deprecated
*/
App.prototype.requireModule = function (name) {
return requireModule(this, name)
}
/**
* get a module of methods for an app instance
*/
export function requireModule (app, name) {
const methods = nativeModules[name]
const target = {}
for (const methodName in methods) {
Object.defineProperty(target, methodName, {
configurable: true,
enumerable: true,
get: function moduleGetter () {
return (...args) => app.callTasks({
module: name,
method: methodName,
args: args
})
},
set: function moduleSetter (value) {
if (typeof value === 'function') {
return app.callTasks({
module: name,
method: methodName,
args: [value]
})
}
}
})
}
return target
}
從 nativeModules 中取到之前注冊時候存入的 methods 遍歷宴倍,將方法的相關(guān)信息設(shè)為 target 的成員张症,最后將 target 返回仓技。調(diào)用的話是直接將 module name、方法名和參數(shù)封裝俗他,傳遞給app.callTasks()
脖捻。
調(diào)用 native
先看看app.callTasks()
的實現(xiàn)。
/**
* @deprecated
*/
App.prototype.callTasks = function (tasks) {
return callTasks(this, tasks)
}
/**
* Call all tasks from an app to renderer (native).
* @param {object} app
* @param {array} tasks
*/
export function callTasks (app, tasks) {
let result
/* istanbul ignore next */
if (typof(tasks) !== 'array') {
tasks = [tasks]
}
tasks.forEach(task => {
result = app.doc.taskCenter.send(
'module',
{
module: task.module,
method: task.method
},
task.args
)
})
return result
}
對 taskCenter 發(fā)送消息兆衅,將 module地沮、method 和 args 都傳遞過去。
export class TaskCenter {
……
send (type, options, args) {
const { action, component, ref, module, method } = options
args = args.map(arg => this.normalize(arg))
switch (type) {
case 'dom':
return this[action](this.instanceId, args)
case 'component':
return this.componentHandler(this.instanceId, ref, method, args, { component })
default:
return this.moduleHandler(this.instanceId, module, method, args, {})
}
}
……
}
export function init () {
const DOM_METHODS = {
createFinish: global.callCreateFinish,
updateFinish: global.callUpdateFinish,
refreshFinish: global.callRefreshFinish,
createBody: global.callCreateBody,
addElement: global.callAddElement,
removeElement: global.callRemoveElement,
moveElement: global.callMoveElement,
updateAttrs: global.callUpdateAttrs,
updateStyle: global.callUpdateStyle,
addEvent: global.callAddEvent,
removeEvent: global.callRemoveEvent
}
const proto = TaskCenter.prototype
for (const name in DOM_METHODS) {
const method = DOM_METHODS[name]
proto[name] = method ?
(id, args) => method(id, ...args) :
(id, args) => fallback(id, [{ module: 'dom', method: name, args }], '-1')
}
proto.componentHandler = global.callNativeComponent ||
((id, ref, method, args, options) =>
fallback(id, [{ component: options.component, ref, method, args }]))
proto.moduleHandler = global.callNativeModule ||
((id, module, method, args) =>
fallback(id, [{ module, method, args }]))
}
taskCenter 這個類是 JS 到 native 的橋梁羡亩,其中的 send 方法摩疑,會根據(jù) type 對發(fā)送來的消息做不同的處理,現(xiàn)在的參數(shù)是 'module' 則調(diào)用moduleHandler()
畏铆。在 init() 函數(shù)中定義了moduleHandler = global.callNativeModule
雷袋,感覺就快找到目標了。
最后終于到callNativeModule
函數(shù)定義在了這里:
@implementation WXJSCoreBridge
……
- (void)registerCallNativeModule:(WXJSCallNativeModule)callNativeModuleBlock
{
_jsContext[@"callNativeModule"] = ^JSValue *(JSValue *instanceId, JSValue *moduleName, JSValue *methodName, JSValue *args, JSValue *options) {
NSString *instanceIdString = [instanceId toString];
NSString *moduleNameString = [moduleName toString];
NSString *methodNameString = [methodName toString];
NSArray *argsArray = [args toArray];
NSDictionary *optionsDic = [options toDictionary];
NSInvocation *invocation = callNativeModuleBlock(instanceIdString, moduleNameString, methodNameString, argsArray, optionsDic);
JSValue *returnValue = [JSValue wx_valueWithReturnValueFromInvocation:invocation inContext:[JSContext currentContext]];
return returnValue;
};
}
……
@end
@implementation WXBridgeContext
……
- (void)registerGlobalFunctions
{
……
[_jsBridge registerCallNativeModule:^NSInvocation*(NSString *instanceId, NSString *moduleName, NSString *methodName, NSArray *arguments, NSDictionary *options) {
WXSDKInstance *instance = [WXSDKManager instanceForID:instanceId];
WXModuleMethod *method = [[WXModuleMethod alloc] initWithModuleName:moduleName methodName:methodName arguments:arguments instance:instance];
return [method invoke];
}];
……
}
……
@end
在 WXJSCoreBridge 中定義了注冊調(diào)用原生模塊的方法辞居,在 WXBridgeContext 中的注冊全局函數(shù)中楷怒,對這個函數(shù)進行了注冊。被調(diào)用的處理很簡單瓦灶,根據(jù) moduleName鸠删、methodName 和 arguments 創(chuàng)建出 WXModuleMethod 并進行調(diào)用。
最后看看 WXModuleMethod 中 invoke 的具體實現(xiàn)贼陶。
- (NSInvocation *)invoke
{
Class moduleClass = [WXModuleFactory classWithModuleName:_moduleName];
id<WXModuleProtocol> moduleInstance = [self.instance moduleForClass:moduleClass];
BOOL isSync = NO;
SEL selector = [WXModuleFactory selectorWithModuleName:self.moduleName methodName:self.methodName isSync:&isSync];
if (![moduleInstance respondsToSelector:selector]) {
// if not implement the selector, then dispatch default module method
if ([self.methodName isEqualToString:@"addEventListener"]) {
[self.instance _addModuleEventObserversWithModuleMethod:self];
} else if ([self.methodName isEqualToString:@"removeAllEventListeners"]) {
[self.instance _removeModuleEventObserverWithModuleMethod:self];
}
return nil;
}
NSInvocation *invocation = [self invocationWithTarget:moduleInstance selector:selector];
if (isSync) {
[invocation invoke];
return invocation;
} else {
[self _dispatchInvocation:invocation moduleInstance:moduleInstance];
return nil;
}
}
先從 WXModuleFactory 中獲取到 moduleClass刃泡,再在 weex instance 中獲取到 moduleInstance,最后得到 invocation 對象并根據(jù)同步異步來做不同的調(diào)用每界,整個 module 的注冊和調(diào)用流程就總算完成了捅僵。