在一名一線開發對於App架構和組件化的思考 文章中,咱們主要站在了軟件工程的角度上,分析了作App架構和組件化時該如何下手,其中也介紹了路由和服務模塊在組件化中扮演的重要角色。本文,咱們將進行實操,一步步實現一個模塊間通訊的服務組件。git
這裏剖出一個微服務的概念,在Java Spring框架中,微服務是個很火的東西。鑑於筆者對於Java一律不知,因此僅僅站在做爲一個App開發的角度去認知它。微服務確切的說是某個功能模塊的子集,它把單體架構中的某些功能拆離出來,而後開啓獨立進程來給其餘模塊提供服務,通訊方式通常是標準REST API來進行。這樣的作的話有幾個好處。github
1.獨立進程,獨立部署。不會由於單體架構機器掛掉後,致使全部服務不可用。
2.避免項目過分臃腫。
3.擴展性強,能夠多個微服務組成集羣。bash
對於Java Spring框架,這裏就不作過多贅述了。推薦一個比較形象的描述微服務的漫畫,感興趣的能夠看一下,這樣能夠對整個系統上下游架構會有更深的理解。cookie
舉個栗子🌰。
仍是拿登陸模塊舉例子。。。框架
在以前的分享中咱們知道,登陸模塊通常位於App分層架構中的通用模塊層。假如說A模塊要調用登陸模塊中的獲取登陸態的方法,在沒有服務組件的狀況下,咱們通常會直接把登陸整個模塊import進來,這樣作不免有點小尷尬(僅僅是獲取個登陸態,我就要把整個登陸模塊import進來,這樣就耦合在一塊兒)。異步
再打個很形象的比喻。。。
雖然說結婚不是兩我的的事情,而是兩個家庭的事情,可是結婚後你老丈人和丈母孃一塊兒打包過來跟你過了,你是什麼感覺?那確定是臉上笑嘻嘻,內心mmp啊。我是要跟你女兒過日子的啊,咋都打包給我了???😄。函數
因此經過以上的生動的示例,咱們總結出了服務組件在App裏的應用場景。微服務
方案一:通知中心(NSNotificationCenter)組件化
Excuse me?通知不是單向數據傳輸麼,A給B發通知,B收到通知後處理,貌似不符合咱們這種有返回值的需求啊?
在OC中有個神奇的東西那就是Block,說白了是匿名函數,那咱們直接把函數指針傳輸過去不就能夠了嘛?並且咱們知道在OC中Block本質上是一個對象,剛好發送通知能夠攜帶一個對象,豈不美哉。
- (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject;
說寫咱就寫!Perfect!你爲什麼如此優秀!!!
登陸模塊:
/*登陸模塊註冊通知*/
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(getCookie) name:@"getCookie" object:nil];
- (void)getCookie:(NSNotification *)noti {
void(^callBack)(NSString *) = noti.object;
/*獲取cookie邏輯*/
---this is a long story---
/*獲取完畢以後,調用block*/
if (callBack) {
callBack(@"cookie");
}
}
複製代碼
調用模塊:
/*建立一個Block*/
void(^callBack)(NSString *) = ^(NSString *cookie) {
NSLog(@"cookie->%@",cookie);
};
/*調用方經過發送通知*/
[[NSNotificationCenter defaultCenter] postNotificationName:@"getCookie" object:callBack];
複製代碼
Command+B,完美!知足需求,咱們成功地在模塊中獲取到登陸模塊中的登陸態。
這時候咱們停下來仔細想一下通知中心的方案,假如說登陸模塊除了提供獲取登陸態的服務,可能還有獲取用戶信息服務等等。若是服務愈來愈多,註冊通知就會分散在不一樣的文件中、不一樣的代碼邏輯中,服務太分散難以維護!!!
咱們總結了一下,很容易發現通知的方案所存在的問題。
關於通知中心的弊端,這裏也不作贅述,推薦一個本身以前寫的一個通知中心解決方案,目前還不太完善。其使用姿式至關優雅,並且實現了異步通知,感興趣的筒子們能夠了解一下。
SmartBlock(一個用Block實現的通知替代方案,而且已實如今不一樣線程進行發送消息和執行Block,支持多參數傳送,解決回調地獄問題,適用於組件化數據傳輸等。)
方案二:反射機制(NSClassFromString)
名詞解釋:Java反射說的是在運行狀態中,對於任何一個類,咱們都可以知道這個類有哪些方法和屬性。對於任何一個對象,咱們都可以對它的方法和屬性進行調用。咱們把這種動態獲取對象信息和調用對象方法的功能稱之爲反射機制。以上內容來自於網上。
在OC中,runtime也提供了相似的機制,咱們能夠經過runtime提供的函數,在運行時動態地獲取到某個類、方法、屬性等。
NSClassFromString(<#NSString * _Nonnull aClassName#>)
NSSelectorFromString(<#NSString * _Nonnull aSelectorName#>)
既然方案一註冊通知太分散,那咱們可不能夠對於每一個服務建立一個類,而後暴露方法,經過runtime反射機制去調用?
登陸模塊:
/*咱們在登陸模塊建立一個GetLoginCookie類*/
/*.h和.m以下*/
複製代碼
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface GetLoginCookie : NSObject
- (id)getLoginCookieWithObjc:(id)obj;
@end
NS_ASSUME_NONNULL_END
複製代碼
#import "GetLoginCookie.h"
@implementation GetLoginCookie
- (id)getLoginCookieWithObjc:(id)obj {
return @"cookie";
}
@end
複製代碼
調用模塊:
/*在模塊中獲取到GetLoginCookie的Class*/
Class cookieCls = NSClassFromString(@"GetLoginCookie");
/*經過Class,生成一個GetLoginCookie實例*/
id cookieInstance = [[cookieCls alloc]init];
/*經過方法名生成一個SEL*/
SEL selector = NSSelectorFromString(@"getLoginCookieWithObjc:");
/*調用performSelector並獲取返回值*/
NSString *cookie = [cookieInstance performSelector:selector withObject:@"it's me!"];
NSLog(@"cookie->%@",cookie);
複製代碼
Command + B,完美運行,咱們也獲得了咱們想要的結果。
對比方案一和方案二,方案二的確解決了服務分散很差管理的問題,可是依然存在幾個問題。
這就比如區塊鏈技術去中心化雖然帶來了不少技術變革,但一樣也帶來了一些隱患。若是沒有上面的👆的監管,那不少black money💰能夠經過區塊鏈手段洗到國外。想一想不少貪官拿着咱們辛辛苦苦繳納的稅,把貪來的錢都洗到了國外,而後老婆孩子在國外逍遙自在,本身在國內作luo官。而咱們依然活在水深火熱之中,百姓民不聊生,苦不堪言,咱們心裏該是何等氣憤!!!😓。
扯多了,咱們回到正題。😄
方案三:引入中間件(IQService)
經過對比前兩個方案,咱們大概對於服務組件應該知足哪些要素有了更加清晰的認識。
咱們來簡單畫一下,服務組件架構圖。
如圖所示,IQService.plist維護了業務list,通常IQService主工程維護一份便可。
LoginModule.plist中維護了該組件爲外部提供的全部服務列表(服務名和實現類的對應關係)
/**
同步、異步調用
@param sevice 微服務名
*/
+ (void)invokeMicroService:(NSString *)sevice,...;
/**
同步調用
@param service 微服務名
@return 同步調用返回值
*/
+ (id)invokeMicroServiceSync:(NSString *)service,...;
複製代碼
咱們再來看下具體的使用姿式。
登陸模塊:
首先建立LoginModuleCookieService類,並將該類註冊到LoginModule中。
複製代碼
.h聲明
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface LoginModuleCookieService : NSObject
- (NSString *)getCookieWithSignature:(NSString *)signature;
@end
NS_ASSUME_NONNULL_END
複製代碼
.m實現
#import "LoginModuleCookieService.h"
@implementation LoginModuleCookieService
- (NSString *)getCookieWithSignature:(NSString *)signature {
return [NSString stringWithFormat:@"%@->cookie",signature];
}
@end
複製代碼
調用模塊:
同步調用
NSString *cookie = [IQService invokeMicroServiceSync:@"GetCookieSyncService",@"我是同步調用",nil];
NSLog(@"%@",cookie);
複製代碼
異步調用
void (^callBack)(NSString *) = ^(NSString *cookie){
NSLog(@"%@",cookie);
};
[IQService invokeMicroService:@"GetCookieAsyncService",@"我是異步調用",callBack,nil];
複製代碼
分析到如今,方案三基本能知足大部分業務需求。具體實現代碼已經開源到GitHub -----> IQService,一個iOS端模塊間通訊的解決方案。喜歡的筒子能夠來波Star❤️,也歡迎你們提交PR和ISSUE。
騙你們刷完Star,如今再潑盆冷水。。。😅
咱們再仔細思考一下方案三,貌似有幾個問題依然沒有解決
1.編譯時依然沒法進行參數正確性校驗,attribute?宏定義?
2.目前只有靜態註冊,不支持動態註冊。
上面兩個問題,歡迎你們進行頭腦風暴。有好的解決方案能夠留言分享,也能夠提交PRs or Issues。github.com/Lobster-Kin…。
在這裏提示一點,沒有一個方案是100%OK的,只有適合本身的纔是最好的。😄
架構和組件化系列文章預告:說說MVVM,會一步步跟你們寫一個輕量的view和viewModel進行數據綁定的框架。
文章首發GitHub github.com/Lobster-Kin…