組件化工具BeeHive(二):組件化實踐

前言

使用BeeHive來進行項目組件化卓舵,其實是使用BeeHive來構建一個中間層均蜜,通過中間層來解耦各個模塊笤休。在文章iOS組件化通用工具淺析有簡單介紹過BeeHive的一些組件化思路齿椅,本文將更多的從使用者的角度來分析BeeHive。

1. 用法

通過構建中間層來組件化項目玻侥,共需要三步:

  1. 創(chuàng)建protocol
  2. 創(chuàng)建impClass
  3. 存儲protocol-impClass映射關系

BeeHive-demo2為例

1.1. 創(chuàng)建protocol

protocol表示模塊對外暴露的接口决摧,調(diào)用模塊時只需要依賴模塊對應的protocol,就可以實現(xiàn)對模塊的調(diào)用凑兰。

下列代碼表示掌桩,模塊A對應的協(xié)議BHServiceProtocol的定義,調(diào)用者可以通過-[getModuleAMainViewController]-[pushToModuleAOneViewController]這兩個方法來調(diào)用模塊A票摇。

// BHServiceProtocol.m

#import "BHServiceProtocol.h"
#import <Foundation/Foundation.h>

@protocol ModuleAServiceProtocol <NSObject, BHServiceProtocol>

- (UIViewController *)getModuleAMainViewController;

- (void)pushToModuleAOneViewController;

@end

這個協(xié)議需要繼承BeeHive中的協(xié)議BHServiceProtocol拘鞋,協(xié)議BHServiceProtocol中定義如下兩個可選方法+[singleton:]+[shareInstance]砚蓬。
如果協(xié)議對應的響應者impClass實現(xiàn)了這兩個方法矢门,并且+[singleton:]方法返回YES,則調(diào)用響應類的+[shareInstance]方法來創(chuàng)建響應者對象。否則祟剔,直接調(diào)用[[implClass alloc] init]來創(chuàng)建對象隔躲。

#import <Foundation/Foundation.h>
#import "BHAnnotation.h"
@protocol BHServiceProtocol <NSObject>

@optional

+ (BOOL)singleton;

+ (id)shareInstance;

@end
1.2. 創(chuàng)建impClass

impClass是protocol對應的響應類,它需要遵守這個protocol協(xié)議物延,它可以是模塊中一個已經(jīng)存在的業(yè)務類宣旱,也可以是這個模塊的一個封裝類。

如果模塊對外暴露的方法全部來自于同一個業(yè)務類叛薯,則可以將這個業(yè)務類設置成impClass浑吟;
如果模塊對外暴露的方法全部來自于多個不同的業(yè)務類,則需要給這個模塊創(chuàng)建一個封裝類耗溜,通過這個封裝類來實現(xiàn)對模塊的調(diào)用组力,impClass指向這個封裝類。(這種方式也叫做target-action)

第一種方式比較常用抖拴,BeeHive的官方demo基本上是使用的這種方法燎字。

模塊A的impClass是ModuleAService類,它是一個封裝類阿宅,內(nèi)部實現(xiàn)了對模塊A中兩個不同類的調(diào)用候衍。

//ModuleAService.m 

#import "ModuleAOneViewController.h"
#import "ModuleAViewController.h"
#import "ModuleAService.h"

@implementation ModuleAService

- (UIViewController *)getModuleAMainViewController{
    return [ModuleAViewController new];
}

- (void )pushToModuleAOneViewController{
    UITabBarController *tab = (UITabBarController *)[UIApplication sharedApplication].delegate.window.rootViewController;
    UINavigationController *nav = tab.selectedViewController;
    ModuleAOneViewController *one = [ModuleAOneViewController new];
    
    [nav pushViewController:one animated:YES];
    
}

另外,模塊C對外暴露的方法只有一個洒放,所以模塊C使用的是第一種方式蛉鹿,它的impClass直接指向ModuleCViewController這個業(yè)務類。

1.3. 設置protocol-impClass映射關系

在BeeHive中拉馋,所有protocol-impClass的映射關系都由BHServiceManager管理榨为,BHServiceManager主要提供了兩個方法:

- (void)registerService:(Protocol *)service implClass:(Class)implClass;
- (id)createService:(Protocol *)service;

方法名中的service指的就是上文中所說的protocol,所以方法一的作用是注冊protocol-impClass的映射關系煌茴,方法二的作用是通過protocol獲取對應的響應類随闺。

BHServiceManager類中,有一個叫做allServicesDict的屬性蔓腐,它保存了所有的protocol-impClass的映射關系矩乐,上述方法一和方法二就是根據(jù)這個屬性來執(zhí)行的。
allServicesDict是一個可變字典回论,其中key是protocol的字符串名稱散罕,value是impClass的字符串名稱。

具體注冊方式有下列三種

1. 使用BeeHive類的-[registerService:service:]

方法-[registerService:service:]的實現(xiàn)

- (void)registerService:(Protocol *)proto service:(Class) serviceClass
{
    [[BHServiceManager sharedManager] registerService:proto implClass:serviceClass];
}

這個方法內(nèi)部就是調(diào)用了BHServiceManager-[registerService:implClass:]方法傀蓉,將傳入的protocolimpClass添加到BHServiceManager類的屬性allServicesDict中欧漱。

2. 使用宏BeeHiveService

上文中,定義了ModuleA模塊的協(xié)議ModuleAServiceProtocol和響應類ModuleAService葬燎,可以使用如下代碼來注冊它們之間的關系:

BeeHiveService(ModuleAServiceProtocol, ModuleAService)

使用宏來注冊時误甚,務必在本模塊中調(diào)用宏缚甩。如果在主工程中調(diào)用,且主工程沒有導入這個模塊(更準確的說是impClass對應的類沒有導入)窑邦,會導致程序crash擅威。

上一篇文章第四節(jié)中已經(jīng)講過了注冊Module類的宏BeeHiveMod,這兩個宏的實現(xiàn)原理是一樣的冈钦,都是在mach-o文件中增加一個section來存儲數(shù)據(jù)郊丛,然后在啟動項目時取出數(shù)據(jù),最終也是調(diào)用BHServiceManager-[registerService:implClass:]方法來注冊瞧筛,詳細過程這里就不在贅述厉熟。

mach-o文件的section:__DATA:BeehiveServices中存儲的是一個json格式的字符串:

"{ \"ModuleAServiceProtocol\" : \"ModuleAService\"}"

3. 使用plist文件

使用plist文件注冊,需要在初始化BeeHive時指定plist文件的路徑

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

plist文件的格式:


需要注意的是BeeHive.bundle必須添加到項目的主工程的target上较幌,因為BeeHive內(nèi)部是在[NSBundle mainBundle]的目錄下尋找BeeHive.bundle庆猫。
當使用cocoapods來加載BeeHive時,默認情況下绅络,BeeHive.bundle是存在于BeeHive.framework中月培,這個時候使用[NSBundle mainBundle]時獲取不到BeeHive.bundle的,解決辦法是改用[NSBundle bundleForClass:self.class]或將BeeHive.bundle添加到項目的主工程的target上恩急。

2. 使用場景

一個典型的場景杉畜,當調(diào)用模塊A時,如果當前還沒有登錄衷恭,則調(diào)用登錄模塊此叠,登錄成功之后,再調(diào)用模塊A随珠;如果已經(jīng)登錄了灭袁,則直接調(diào)用模塊A。

以項目BeeHive-demo3為例

模塊A對外的協(xié)議ModuleAServiceProtocol

//BHServiceProtocol.h

#import "BHServiceProtocol.h"
#import <Foundation/Foundation.h>

@protocol ModuleAServiceProtocol <NSObject, BHServiceProtocol>

- (void)pushToModuleAViewController;

@end

模塊A的響應類

//ModuleAService.m

#import "ModuleAViewController.h"
#import "ModuleAService.h"

@BeeHiveService(ModuleAServiceProtocol, ModuleAService)
@implementation ModuleAService

- (void )pushToModuleAViewController{
    
    id<LoginServiceProtocol> moduleAService = [[BeeHive shareInstance] createService:@protocol(LoginServiceProtocol)];
    
    [moduleAService loginIfNeedWithCompleteBlock:^(BOOL succeed) {
        if (succeed) {
            UINavigationController *root = (UINavigationController *)[UIApplication sharedApplication].delegate.window.rootViewController;
            ModuleAViewController *moduleA = [ModuleAViewController new];

            [root pushViewController:moduleA animated:YES];
        }
    }];
}
@end

不管有沒有登錄窗看,首先調(diào)用登錄模塊茸歧,具體的跳轉邏輯被保存在block中,然后傳給登錄模塊显沈,登錄完成之后软瞎,執(zhí)行這個block。

登錄模塊的協(xié)議LoginServiceProtocol

//LoginServiceProtocol.h

#import "BHServiceProtocol.h"
#import <Foundation/Foundation.h>

@protocol LoginServiceProtocol <NSObject, BHServiceProtocol>

- (void)loginIfNeedWithCompleteBlock:(void (^)(BOOL))completeBlock;

@end

登錄模塊的響應類

//LoginService.m 

#import "LoginViewController.h"
#import "LoginService.h"

@BeeHiveService(LoginServiceProtocol, LoginService)
@implementation LoginService

- (void)loginIfNeedWithCompleteBlock:(void (^)(BOOL))completeBlock{
    if ([LoginViewController isLogined]) {
        completeBlock(YES);

    }else{
        LoginViewController *login = [LoginViewController new];
        login.completeBlock = completeBlock;
        
        UIViewController *root = [UIApplication sharedApplication].delegate.window.rootViewController;
        [root presentViewController:login animated:YES completion:nil];
    }
}

@end

如果已經(jīng)登錄拉讯,直接執(zhí)行傳入的block涤浇;如果沒有登錄,則彈出登錄界面魔慷,登錄成功之后只锭,執(zhí)行block。

3. impClass的生命周期

通過上文可知院尔,impClass的對象是最終是由BHServiceManager類創(chuàng)建的蜻展,但是BHServiceManager類并沒有持有impClass的對象页滚,本質上,BHServiceManager相當于是一個對象工廠铺呵。

如果impClass是一個模塊的封裝類,impClass的對象只在當前作用域有效隧熙,超過了這個作用域片挂,這個對象會被釋放掉。
如果impClass是一個模塊的業(yè)務類贞盯,則impClass對象的生命周期依賴于模塊內(nèi)部的具體實現(xiàn)了音念。

如果想長期持有這個impClass對象,通常有兩種方式:

1.在模塊調(diào)用處躏敢,強引用被創(chuàng)建的impClass對象闷愤。

2.實現(xiàn)BeeHive中BHServiceProtocol協(xié)議的+[singleton]方法,并返回YES件余。這樣讥脐,被創(chuàng)建的impClass對象會被保存在單例[BHContext shareInstance]中。(如果同時實現(xiàn)了+[shareInstance]方法啼器,則使用這個方法來創(chuàng)建impClass的對象)

可以使用下列BHContext的方法來移除保存的impClass對象

- (void)removeServiceWithServiceName:(NSString *)serviceName;

4. 異常處理

BeeHive可以通過下列設置來開啟異常模式旬渠,在這個模式下,如果遇到BeeHive內(nèi)部的一些錯誤端壳,會直接拋出異常告丢。一般在調(diào)試模式下,應該開啟损谦。生產(chǎn)模式下岖免,應該關閉。

[BeeHive shareInstance].enableException = YES;
[[BeeHive shareInstance] setContext:[BHContext shareInstance]];
4.1 注冊時異常

注冊方式共有三種:

  1. 使用BeeHive類的-[registerService:service:]
  2. 使用宏BeeHiveService
  3. 使用plist文件

注冊時照捡,可能存在下列三種情況:

  1. protocol和impClass對應的協(xié)議或類不存在
  2. protocol和impClass存在颅湘,但impClass沒有遵循對應的protocol
  3. protocol和impClass存在,且impClass遵循對應的protocol
方式一 方式二 方式三
情況一 編譯時報錯 啟動時crash 注冊成功
情況二 注冊不成功栗精,如果是異常模式栅炒,則crash 注冊不成功,如果是異常模式术羔,則crash 注冊成功
情況二 注冊成功 注冊成功 注冊成功

當注冊方法和被注冊的模塊沒有寫在一起時赢赊,刪除了模塊,而它的注冊方法沒有被刪除级历,這個時候就會出現(xiàn)情況一释移,比如在pod中解除了對模塊的依賴。
要避免情況一中的兩個報錯寥殖,最好是將注冊方法寫在本模塊中玩讳,比如Module類的-[modInit:]方法中涩蜘,這樣刪除模塊的時候,也刪除了對應的注冊方法熏纯。

不管plist文件中protocol和impClass是否存在同诫,是否匹配,只要它們的key符合格式樟澜,就會被注冊成功误窖。

4.2. 調(diào)用時異常

在調(diào)用模塊時,首先需要創(chuàng)建impClass秩贰,一般是通過BeeHive類的-[createService:]方法霹俺,這個方法需要一個protocol

- (id)createService:(Protocol *)proto;

創(chuàng)建好impClass的對象之后,然后這個對象調(diào)用protocol中聲明的方法毒费。

在這個調(diào)用過程中丙唧,可能會遇到下列三種情況:

protocol未注冊 protocol已注冊,但對應impClass的類不存在 protocol已注冊觅玻,且對應impClass的類存在想际,但執(zhí)行的方法沒實現(xiàn)
處理結果 將impClass的值設置為nil,如果是異常模式溪厘,則crash沼琉。 將impClass的值設置為nil 拋出異常
4.3. 小結

在調(diào)試階段時,可以開啟異常模式桩匪,這樣就能檢測一些潛在的問題出來打瘪,比如impClass沒有遵循protocol、使用未注冊的protocol來創(chuàng)建impClass傻昙。

關于異常處理闺骚,需要注意的是,impClass必須實現(xiàn)被調(diào)用的方法妆档。另外僻爽,將注冊方法寫在本模塊中。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末贾惦,一起剝皮案震驚了整個濱河市胸梆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌须板,老刑警劉巖碰镜,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異习瑰,居然都是意外死亡绪颖,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進店門甜奄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來柠横,“玉大人窃款,你說我怎么就攤上這事‰狗眨” “怎么了晨继?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長搬俊。 經(jīng)常有香客問我紊扬,道長,這世上最難降的妖魔是什么悠抹? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮扩淀,結果婚禮上楔敌,老公的妹妹穿的比我還像新娘。我一直安慰自己驻谆,他們只是感情好卵凑,可當我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著胜臊,像睡著了一般勺卢。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上象对,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天黑忱,我揣著相機與錄音,去河邊找鬼勒魔。 笑死甫煞,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的冠绢。 我是一名探鬼主播抚吠,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼弟胀!你這毒婦竟也來了楷力?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤孵户,失蹤者是張志新(化名)和其女友劉穎萧朝,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體夏哭,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡剪勿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了方庭。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片厕吉。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡酱固,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出头朱,到底是詐尸還是另有隱情运悲,我是刑警寧澤,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布项钮,位于F島的核電站班眯,受9級特大地震影響,放射性物質發(fā)生泄漏烁巫。R本人自食惡果不足惜署隘,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望亚隙。 院中可真熱鬧磁餐,春花似錦、人聲如沸阿弃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽渣淳。三九已至脾还,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間入愧,已是汗流浹背鄙漏。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留棺蛛,地道東北人泥张。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像鞠值,于是被迫代替她去往敵國和親媚创。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,927評論 2 355

推薦閱讀更多精彩內(nèi)容