組件化工具BeeHive(一):事件分發(fā)

前言

BeeHive是阿里開源的一個組件化框架工具,其內(nèi)部是使用Spring框架Service的理念來實現(xiàn)模塊解耦的,實際上就是使用protocol-class的方案。另外,在組件化的基礎(chǔ)上,BeeHive還增加了一個事件分發(fā)的功能來配合使用。

目錄

  • 1. 概覽
  • 2. 事件分發(fā)的作用
  • 3. 事件分發(fā)的配置過程
  • 4. 注冊Module的幾種方式
  • 5. 事件分發(fā)的內(nèi)部實現(xiàn)
  • 6. 事件種類

1. 概覽

官方文檔中的架構(gòu)圖:


從上圖可以看出,BeeHive的工作分為兩部分:

  1. 事件分發(fā)
    BeeHive本身會監(jiān)聽一些系統(tǒng)事件和應(yīng)用事件,比如App生命周期、推送、handoff等,當(dāng)事件發(fā)生時,BeeHive將其分發(fā)給各個模塊,然后各個業(yè)務(wù)模塊就可以在自己的Module類中調(diào)用各自的響應(yīng)方法。

  2. 組件化
    這部分是指在組件化的情況下,實現(xiàn)模塊間調(diào)用,也就是說,各個模塊是相互解耦的,BeeHive使用protocol-class的方案實現(xiàn)這一點。

2. 事件分發(fā)的作用

當(dāng)一個事件被觸發(fā)時,其對應(yīng)的響應(yīng)方法需要被執(zhí)行,比如界面更新、數(shù)據(jù)存儲等。在這個過程中,會涉及到響應(yīng)方法的調(diào)用和實現(xiàn)這兩部分。

首先需要確定的是響應(yīng)方法的實現(xiàn)都是由模塊來完成的(不屬于現(xiàn)有模塊的響應(yīng)方法可以看做是屬于一個全局模塊),針對調(diào)用響應(yīng)方法的位置,這里就有兩種調(diào)用方式,一個是直接在事件觸發(fā)點調(diào)用(通常是在AppDelegate),另一個是通過BeeHive將事件分發(fā)給各個模塊,在具體模塊的Module類中調(diào)用。

為了對比這兩種調(diào)用方法的差別,下面以一個例子來說明:

一個場景

用戶在spotlight搜索一個關(guān)鍵字testA,點擊搜索結(jié)果,app需要跳轉(zhuǎn)到模塊A中的一個界面;
搜索關(guān)鍵字testB,點擊搜索結(jié)果,app需要跳到模塊B中的一個界面。

具體操作如下:


在這個場景中,當(dāng)用戶在spotlight中點擊一個搜索結(jié)果后,會觸發(fā)一個handoff事件,這個事件會被AppDelegate接受到,可以把AppDelegate當(dāng)做是事件的觸發(fā)點。
這個事件期望的響應(yīng)是,根據(jù)點擊的搜索結(jié)果,跳轉(zhuǎn)到對應(yīng)的模塊界面中。

2.1. 直接調(diào)用

直接調(diào)用事件的響應(yīng)方法,代碼如下:(完整項目代碼可以查看BeeHive_demo1

- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler{
    
    if ([userActivity.activityType isEqualToString:@"com.company.app.moduleA.one"]) {
        
        id<ModuleAServiceProtocol> moduleAService = [[BeeHive shareInstance] createService:@protocol(ModuleAServiceProtocol)];
        [moduleAService pushToModuleAOneViewController];
        
    }else if ([userActivity.activityType isEqualToString:@"com.company.app.moduleB.one"]) {
        
        id<ModuleBServiceProtocol> moduleBService = [[BeeHive shareInstance] createService:@protocol(ModuleBServiceProtocol)];
        [moduleBService pushToModuleBOneViewController];
        
    }
    return YES;
}

上述代碼中BeeHive的createService:方法是用來獲取對應(yīng)的模塊句柄(下文會具體講到),使用這個句柄可以直接調(diào)用模塊的響應(yīng)方法,跳轉(zhuǎn)到模塊對應(yīng)的界面。
根據(jù)userActivity的類型來判斷app是跳轉(zhuǎn)到moduleA的界面還是moduleB的界面。

直接調(diào)用存在的問題

  • 在事件觸發(fā)點處(AppDelegate)直接調(diào)用模塊的響應(yīng)方法,會將事件的響應(yīng)代碼全都堆積在觸發(fā)點處,當(dāng)事件的類別越來越多,這個地方會存在許多判斷語句,不便于閱讀和維護(hù);
    更重要的是會導(dǎo)致觸發(fā)點對各個模塊產(chǎn)生依賴,繼而會影響觸發(fā)點的穩(wěn)定性,只要模塊稍有改動,觸發(fā)點也要跟著變動。

  • 在執(zhí)行事件的響應(yīng)方法的過程中,會涉及到響應(yīng)方法的調(diào)用邏輯和實現(xiàn)邏輯這兩部分。響應(yīng)方法的實現(xiàn)邏輯通常是在模塊中完成的,采用第一種方式,響應(yīng)方法的調(diào)用邏輯會在觸發(fā)點處完成,整個事件的處理過程會被分割在觸發(fā)點和模塊這兩部分中,當(dāng)需要對事件的響應(yīng)邏輯做出變動時,則需要在這兩部分同時做出改變。
    事件觸發(fā)點一般是位在主工程中,在大型項目中,模塊和主工程一般是分開開發(fā)的,并且可能是由不同的開發(fā)者開發(fā)的,一個事件最好不要同時涉及到這兩部分,因為這樣會導(dǎo)致這兩部分存在某種耦合,不易于維護(hù)。

2.2. 事件分發(fā)

在使用BeeHive之后,BeeHive會監(jiān)聽這些事件,事件觸發(fā)后,它會遍歷已注冊模塊對應(yīng)的Module類,然后調(diào)用這些類對應(yīng)的事件響應(yīng)方法,這樣,BeeHive就將一個事件分發(fā)給了所有的Module類。
換句話說,就是給每一個需要響應(yīng)事件的模塊都新建一個對應(yīng)的Module類,在這個Module類中完成對響應(yīng)方法的調(diào)用,這個Module類和模塊將由同一個開發(fā)者創(chuàng)建和維護(hù)。

這樣,一個事件的整個響應(yīng)過程就都是由模塊負(fù)責(zé),主工程只需要負(fù)責(zé)事件分發(fā)。事件的處理被隔離在模塊內(nèi)部,即便以后需要修改事件的響應(yīng)邏輯,也只需要改動模塊,主工程不需要任何改動。

3. 事件分發(fā)的配置過程

使用BeeHive實現(xiàn)事件分發(fā),共需要三步,下面還是以上文的場景來作為例子進(jìn)行講解:(完整項目代碼可以查看BeeHive_demo2

  1. 初始化
    BeeHive內(nèi)部有一個類BHAppDelegate,它的作用就是監(jiān)聽事件的觸發(fā),實際項目中的AppDelegate需要繼承這個類。
//AppDelegate.m

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    [BHContext shareInstance].application = application;
    [BHContext shareInstance].launchOptions = launchOptions;
    [BHContext shareInstance].moduleConfigName = @"BeeHive.bundle/BeeHive";
    [BHContext shareInstance].serviceConfigName = @"BeeHive.bundle/BHService";
    
    [BeeHive shareInstance].enableException = YES;
    [[BeeHive shareInstance] setContext:[BHContext shareInstance]];
    
    [super application:application didFinishLaunchingWithOptions:launchOptions];
    ...
    ...
    return YES;
}
  1. 創(chuàng)建并注冊Module
    每一個需要響應(yīng)事件的模塊,都需要新建一個對應(yīng)的Module類,且Module類需要遵守協(xié)議BHModuleProtocol,然后使用BeeHive提供的方法將這個Module類注冊到BeeHive中,這樣,BeeHive才能將事件轉(zhuǎn)發(fā)給這個模塊。
#import "ModuleAModule.h"
#import "BHService.h"

//注冊
@BeeHiveMod(ModuleAModule)
@interface ModuleAModule() <BHModuleProtocol>
@end

@implementation ModuleAModule
...
...
@end

本例中使用宏BeeHiveMod來注冊ModuleAModule類,代碼如下:

@BeeHiveMod(ModuleAModule)
  1. 調(diào)用響應(yīng)方法
    Module類中,調(diào)用事件對應(yīng)的響應(yīng)方法,每個Module類只應(yīng)該處理和本模塊相關(guān)的事件。
    在[步驟2]創(chuàng)建的Module類中添加響應(yīng)方法:
//handoff事件響應(yīng)
- (void)modContinueUserActivity:(BHContext *)context{
    
    NSUserActivity *userActivity = context.userActivityItem.userActivity;
    if ([userActivity.activityType isEqualToString:@"com.company.app.moduleA.one"]) {
    
        id<ModuleAServiceProtocol> moduleAService = [[BeeHive shareInstance] createService:@protocol(ModuleAServiceProtocol)];
        [moduleAService pushToModuleAOneViewController];
    }
}

4. 注冊Module的幾種方式

BeeHive提供了四種方式來注冊成為Module類,其內(nèi)部實現(xiàn)大多數(shù)都是間接調(diào)用:

[[BHModuleManager sharedManager] registerDynamicModule:moduleClass]
4.1. 方式一

通過BeeHive類的類方法+ (void)registerDynamicModule:(Class) moduleClass;
其方法實現(xiàn)是直接將消息轉(zhuǎn)發(fā)給BHModuleManager類:

+ (void)registerDynamicModule:(Class)moduleClass
{
    [[BHModuleManager sharedManager] registerDynamicModule:moduleClass];
}
4.2. 方式二

使用在協(xié)議BHModuleProtocol中定義的宏BH_EXPORT_MODULE,其定義為:

#define BH_EXPORT_MODULE(isAsync) \
+ (void)load { [BeeHive registerDynamicModule:[self class]]; } \
-(BOOL)async { return [[NSString stringWithUTF8String:#isAsync] boolValue];}

這種方式是對第一種方式的調(diào)用,同時還增加了對-[async]方法的定義,返回YES表示這個Module類將會被異步加載,用來優(yōu)化啟動時間。Module類都需要遵守這個協(xié)議BHModuleProtocol,所以可以在其內(nèi)部直接使用這個宏。

4.3. 方式三

使用在BHAnnotation類中定義的宏BeeHiveMod,這個宏需要一個類名作為參數(shù),其用法如下:

@BeeHiveMod(ModuleAModule)

只需要這一句代碼,ModuleAModule類就會被注冊成功。

如何實現(xiàn)?

使用這個宏@BeeHiveMod(ModuleAModule),整個注冊過程會分為三步

  1. 存儲需要注冊的類名
    BeeHiveMod的作用就是存儲類名,它會在項目mach-o文件的segment:__DATA中添加一個名為BeehiveMods的section,并將字符串"ModuleAModule"添加到這個section中。

  2. 取出存儲的類名
    從項目mach-o文件的(__DATA,BeehiveMods)中,獲取所有的類名。

  3. 注冊
    使用[步驟2]的類名,調(diào)用BHModuleManager類的registerDynamicModule:方法來注冊。

4.3.1. 存儲類名

BeeHiveMod的定義

#define BeeHiveMod(name) \
class BeeHive; char * k##name##_mod BeeHiveDATA(BeehiveMods) = ""#name"";
#define BeeHiveDATA(sectname) __attribute((used, section("__DATA,"#sectname" ")))

在編譯的時,預(yù)處理器會根據(jù)分段標(biāo)志將被定義的字符串進(jìn)行分段,然后每一段都會和宏的參數(shù)對比,如果相同就會被替換,空格、括號、運算符和##等都屬于一種分段標(biāo)志。
##一般被用在被替換段和其他段直接接觸的情況下,比如替換變量名的一部分,上述宏定義中,就是使用兩個##將一個變量名隔離成三段,kname_mod,然后使用參數(shù)替換name那一段。

上述宏定義中,還用到了#符號,#后面跟著的必須是宏的參數(shù),它的作用是將參數(shù)的值符號化,也就是用一對引號""將參數(shù)包圍起來。
另外,需要注意的是在使用#進(jìn)行符號化的時候,其前面和后面的引號的數(shù)量必須是偶數(shù),否則,預(yù)處理器不會替換和符號化參數(shù)。

下面使用預(yù)處理命令來驗證一下:

新建一個macro.c文件,將上述宏定義寫入,并調(diào)用

//macro.c

#define BeeHiveDATA(sectname) __attribute((used, section("__DATA, "#sectname" ")))

#define BeeHiveMod(name) \
class BeeHive; char * k##name##_mod BeeHiveDATA(BeehiveMods) = ""#name"";


@BeeHiveMod(ModuleAModule)

在終端輸入如下預(yù)處理處理命令,預(yù)處理階段會進(jìn)行宏解析

clang -E macro.c 

輸出的關(guān)鍵部分

@class BeeHive; char * kModuleAModule_mod __attribute((used, section("__DATA, ""BeehiveMods"" "))) = """ModuleAModule""";

上述輸出中,__attribute((used, section("__DATA, ""BeehiveMods"" ")))表示在項目的mach-o文件的名字為__DATA的segment中添加一個名字為BeehiveMods的section,并將其值設(shè)置為字符串"ModuleAModule"

下面使用otool命令來驗證一下

首先找到本文項目BeeHive-demo2生成的mach-o文件,在終端執(zhí)行如下命令來輸出這個mach-o文件的所有segment和section:

otool -l BeeHive-demo2 

下面是這個命令的部分輸出:

......
Section
  sectname BeehiveMods
   segname __DATA
      addr 0x000000010002b3b8
      size 0x0000000000000010
    offset 177080
     align 2^3 (8)
    reloff 0
    nreloc 0
     flags 0x00000000
 reserved1 0
 reserved2 0
......

在mach-o文件中確實存在BeehiveMods這個section,接下來,繼續(xù)驗證這個section中的值是否是字符串"ModuleAModule"。

使用下來命令查看section的內(nèi)容:

otool -s __DATA BeehiveMods BeeHive-demo2

__DATA表示segment,BeehiveMods表示section,其輸出為:

BeeHive-demo2:
Contents of (__DATA,BeehiveMods) section
000000010002b3b8    0001ea48 00000001 0001ead2 00000001 

上述輸出表示,section內(nèi)部包含了兩個地址,分別為010001ea48010001ead2,這兩個地址的其中一個就是指向字符串"ModuleAModule"。

為了找到這兩個地址指向的具體值,可以使用下列命名查看mach-o文件中segment為__TEXT,section為__cstring的內(nèi)容

otool -V -s __TEXT __cstring BeeHive-demo2 

截取輸出的開頭部分

BeeHive-demo2:
Contents of (__TEXT,__cstring) section
000000010001ea48  ModuleBModule
000000010001ea56  com.company.app.moduleB.one
000000010001ea72  hash
000000010001ea77  TQ,R
000000010001ea7c  superclass
000000010001ea87  T#,R
000000010001ea8c  description
000000010001ea98  T@\"NSString\",R,C
000000010001eaa9  debugDescription
000000010001eaba  moduleA
000000010001eac2  moduleB
000000010001eaca  moduleC
000000010001ead2  ModuleAModule
000000010001eae0  com.company.app.moduleA.one

可以看到010001ea48010001ead2這兩個地址分別在上述輸出中的第三行和倒數(shù)第二行,對應(yīng)的字符串分別為"ModuleBModule""ModuleAModule"。

這樣,類名就被存儲在mach-o文件的section中了。

4.3.2. 取出類名

BeeHive在文件BHAnnotation.m中注冊了一個函數(shù)dyld_callback,代碼如下:

__attribute__((constructor))
void initProphet() {
    _dyld_register_func_for_add_image(dyld_callback);
}

當(dāng)一個函數(shù)被__attribute__((constructor))修飾時,表示這個函數(shù)是這個image的初始化函數(shù),在image被加載時,首先會調(diào)用這個函數(shù)。(image指的是mach-o和動態(tài)共享庫,在工程運行時,可以使用lldb命令image list查看這個工程中加載的所有image。)
上述代碼表示initProphet函數(shù)被指定為mach-o的初始化函數(shù),當(dāng)dyld(動態(tài)鏈接器)加載mach-o時,執(zhí)行initProphet函數(shù),其執(zhí)行時機(jī)在man函數(shù)和類的load方法之前。

當(dāng)_dyld_register_func_for_add_image(dyld_callback);被執(zhí)行時,如果已經(jīng)加載了image,則每存在一個已經(jīng)加載的image就執(zhí)行一次dyld_callback函數(shù),在此之后,每當(dāng)有一個新的image被加載時,也會執(zhí)行一次dyld_callback函數(shù)。
dyld_callback函數(shù)在image的初始化函數(shù)之前被調(diào)用,mach-o是第一個被加載的image,調(diào)用順序是:load mach-o -> initProphet -> dyld_callback -> load other_image -> dyld_callback -> other_image_initializers -> ......)

所以,當(dāng)程序啟動時,會多次調(diào)用dyld_callback函數(shù)。

dyld_callback函數(shù)中,使用下列函數(shù)來獲取[步驟2]中存儲的類名

extern uint8_t *getsectiondata(
    const struct mach_header_64 *mhp,
    const char *segname,
    const char *sectname,
    unsigned long *size);

segname的值為__DATA,sectname的值為BeehiveMods。

4.4.3. 注冊

dyld_callback函數(shù)中,調(diào)用BHModuleManager的注冊方法,并傳入上文中回去的類名

[[BHModuleManager sharedManager] registerDynamicModule:cls];

4.4. 方式四

使用plist文件注冊,首先需要指定plist文件的路徑,使用如下代碼來指定路徑:

[BHContext shareInstance].moduleConfigName = @"BeeHive.bundle/BeeHive";

這一句代碼一般是在初始化BeeHive時調(diào)用

plist文件的格式:

需要注意的是BeeHive.bundle必須添加到項目的主工程的target上,因為BeeHive內(nèi)部是在[NSBundle mainBundle]的目錄下尋找BeeHive.bundle。
當(dāng)使用cocoapods來加載BeeHive時,默認(rèn)情況下,BeeHive.bundle是存在于BeeHive.framework中,這個時候使用[NSBundle mainBundle]時獲取不到BeeHive.bundle的,解決辦法是改用[NSBundle bundleForClass:self.class]或?qū)eeHive.bundle添加到項目的主工程的target上。

5. 事件分發(fā)的內(nèi)部實現(xiàn)

在BeeHive中,使用BHModuleManager類來負(fù)責(zé)事件分發(fā),其主要步驟是:

  1. 注冊
    存儲Module類的對象和事件對應(yīng)的響應(yīng)方法
  2. 觸發(fā)
    通過事件類型,獲取需要響應(yīng)的Module類對象和對應(yīng)的響應(yīng)方法,使用performSelector:withObject:執(zhí)行響應(yīng)方法。
5.1. 注冊

BHModuleManager使用四個實例屬性來存儲Module類的對象和對應(yīng)的響應(yīng)方法

@property(nonatomic, strong) NSMutableArray<NSDictionary *>     *BHModuleInfos;
@property(nonatomic, strong) NSMutableArray     *BHModules;

@property(nonatomic, strong) NSMutableDictionary<NSNumber *, NSMutableArray<id<BHModuleProtocol>> *> *BHModulesByEvent;
@property(nonatomic, strong) NSMutableDictionary<NSNumber *, NSString *> *BHSelectorByEvent;

BHModules屬性存儲了所有注冊的Module類的對象。

BHModuleInfos屬性保存了對應(yīng)Module類的一些狀態(tài),比如kModuleInfoHasInstantiatedKey表示Module類是否已創(chuàng)建,每一個Module類對應(yīng)其內(nèi)部的一個字典。

BHModulesByEvent屬性表示每當(dāng)一個事件觸發(fā)時,哪些Module類需要響應(yīng)這個事件。它是一個字典類型,以事件類型eventType作為key,響應(yīng)這個事件的Module類的對象組成的數(shù)組作為value。

BHSelectorByEvent屬性存儲了事件類型和響應(yīng)方法名的映射關(guān)系,它是一個字典類型,以事件類型eventType作為key,事件的響應(yīng)方法的字符串名作為value。

相對比較重要的是后面兩個屬性,當(dāng)事件觸發(fā)時,會使用這兩個屬性。

這四個屬性的值都是在注冊Module類時設(shè)置的,從上文可知,注冊Module類時,其內(nèi)部是調(diào)用BHModuleManager類的registerDynamicModule:方法。
registerDynamicModule:方法的內(nèi)部,首先將傳入的Module類參數(shù)實例化得到一個對象moduleInstance,然后將其添加到BHModules中,并創(chuàng)建一個對應(yīng)的字典添加到BHModuleInfos中。(這里就存儲好了BHModulesBHModuleInfos這兩個屬性。)

最后,調(diào)用registerEventsByModuleInstance:來給moduleInstance對象注冊響應(yīng)事件。

- (void)registerEventsByModuleInstance:(id<BHModuleProtocol>)moduleInstance
{
    NSArray<NSNumber *> *events = self.BHSelectorByEvent.allKeys;
    [events enumerateObjectsUsingBlock:^(NSNumber * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        [self registerEvent:obj.integerValue withModuleInstance:moduleInstance andSelectorStr:self.BHSelectorByEvent[obj]];
    }];
}

BHSelectorByEvent屬性存儲了所有事件類型和響應(yīng)方法名的映射關(guān)系,遍歷這個屬性的所有事件類型,通過事件類型拿到BHModulesByEvent屬性對應(yīng)的響應(yīng)方法列表,然后將moduleInstance添加到事件的響應(yīng)方法列表中。(這里就存儲好了BHModulesByEvent屬性)

如果需要添加自定義事件類型,也就是在BHSelectorByEvent屬性中添加一個映射關(guān)系,可以使用方法registerCustomEvent:withModuleInstance:andSelectorStr:

5.2. 觸發(fā)

使用BeeHive時,事件是如何被觸發(fā)的?

這里還是以handoff為例,查看BeeHive中的BHAnnotation類的handoff代理方法:

- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler
{
    if([UIDevice currentDevice].systemVersion.floatValue >= 8.0f){
        //保存參數(shù)
        [[BeeHive shareInstance].context.userActivityItem setUserActivity: userActivity];
        [[BeeHive shareInstance].context.userActivityItem setRestorationHandler: restorationHandler];
        //分發(fā)事件
        [[BHModuleManager sharedManager] triggerEvent:BHMContinueUserActivityEvent];
    }
    return YES;
}

可以看出,事件是通過BHModuleManager類的triggerEvent:方法分發(fā)出去的。triggerEvent:方法接受了一個參數(shù)BHMContinueUserActivityEvent,用來表示事件的類型。

查看triggerEvent:的內(nèi)部實現(xiàn),發(fā)現(xiàn)最終會調(diào)用BHModuleManager類的方法handleModuleEvent:forTarget:withSeletorStr:andCustomParam:

- (void)handleModuleEvent:(NSInteger)eventType
                forTarget:(id<BHModuleProtocol>)target
           withSeletorStr:(NSString *)selectorStr
           andCustomParam:(NSDictionary *)customParam
{
    BHContext *context = [BHContext shareInstance].copy;
    context.customParam = customParam;
    context.customEvent = eventType;
    if (!selectorStr.length) {
        selectorStr = [self.BHSelectorByEvent objectForKey:@(eventType)];
    }
    SEL seletor = NSSelectorFromString(selectorStr);
    if (!seletor) {
        selectorStr = [self.BHSelectorByEvent objectForKey:@(eventType)];
        seletor = NSSelectorFromString(selectorStr);
    }
    NSArray<id<BHModuleProtocol>> *moduleInstances;
    if (target) {
        moduleInstances = @[target];
    } else {
        moduleInstances = [self.BHModulesByEvent objectForKey:@(eventType)];
    }
    [moduleInstances enumerateObjectsUsingBlock:^(id<BHModuleProtocol> moduleInstance, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([moduleInstance respondsToSelector:seletor]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
            [moduleInstance performSelector:seletor withObject:context];
#pragma clang diagnostic pop
            
            [[BHTimeProfiler sharedTimeProfiler] recordEventTime:[NSString stringWithFormat:@"%@ --- %@", [moduleInstance class], NSStringFromSelector(seletor)]];
            
        }
    }];
}

這個方法的目的是執(zhí)行[moduleInstance performSelector:seletor withObject:context],其中moduleInstanceseletor表示事件對應(yīng)的Module類對象和響應(yīng)方法,它們分別可以由參數(shù)target和參數(shù)selectorStr來指定,由于在本例中并沒有傳入這兩個參數(shù),只傳入了BHMContinueUserActivityEvent作為參數(shù)eventType的值,所以它們需要根據(jù)參數(shù)eventTypeself.BHModulesByEventself.BHSelectorByEvent這兩個屬性中獲取,這里的self指的是BHModuleManager,它們都是在注冊時就設(shè)置好了。

6. 事件類型

官方的系統(tǒng)事件流程圖:


上圖中的事件包含了Application生命周期事件和BeeHive自己擴(kuò)展的三個事件,ModSetup、ModInit、ModSplash。除了這些事件,BeeHive還監(jiān)聽了推送、3D-Touch等相關(guān)的事件。

其完整的事件如下:

typedef NS_ENUM(NSInteger, BHModuleEventType)
{
    //通用事件
    BHMSetupEvent = 0,
    BHMInitEvent,
    BHMTearDownEvent,
    BHMSplashEvent,
    
    //3D-Touch
    BHMQuickActionEvent,
    
    //生命周期
    BHMWillResignActiveEvent,
    BHMDidEnterBackgroundEvent,
    BHMWillEnterForegroundEvent,
    BHMDidBecomeActiveEvent,
    BHMWillTerminateEvent,
    
    //未使用
    BHMUnmountEvent,
    BHMOpenURLEvent,
    BHMDidReceiveMemoryWarningEvent,

    //推送相關(guān)事件
    BHMDidFailToRegisterForRemoteNotificationsEvent,
    BHMDidRegisterForRemoteNotificationsEvent,
    BHMDidReceiveRemoteNotificationEvent,
    BHMDidReceiveLocalNotificationEvent,
    BHMWillPresentNotificationEvent,
    BHMDidReceiveNotificationResponseEvent,

    //handoff和相關(guān)事件
    BHMWillContinueUserActivityEvent,
    BHMContinueUserActivityEvent,
    BHMDidFailToContinueUserActivityEvent,
    BHMDidUpdateUserActivityEvent,

    //watchApp請求事件
    BHMHandleWatchKitExtensionRequestEvent,

    //自定義事件
    BHMDidCustomEvent = 1000
    
};
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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