iOS使用“注解”實現(xiàn)“微服務(wù)”路由

閱讀本文大概需要 4.55 分鐘

前言

大家知道 Objective-C 本身是沒有支持注解功能的容客,但有時使用注解將大幅提高效率,同時讓代碼更簡單易懂。特別是今天要介紹的一個關(guān)于“微服務(wù)”注冊的場景棒假。

什么是“微服務(wù)”

微服務(wù)是目前后端提的比較多的一個東西,從廣義上來說就是一個去中心化的開發(fā)模式精盅,通過各個組件的自注冊帽哑,達到服務(wù)分發(fā)的效果。

那么跟客戶端有什么關(guān)系呢叹俏?有一個很具體的例子就是“界面路由”妻枕。這也是近期大家談的比較多的一個事(包括我最近也在做),相信很多同學(xué)并不陌生她肯。具體的做法就是佳头,我們會對各個界面定義一個ID,比如首頁-main晴氨、詳情頁-detail康嘉、關(guān)于頁-about等等,然后通過路由管理器來分發(fā)籽前,從而展示指定的Controller亭珍。

路由方案一

一個比較簡單的做法是建立ID到Controller的映射表,然后根據(jù)Controller類名創(chuàng)建對象枝哄,進行push操作:

NSString *controllerClazz = [routeConfig clazzForID:@"main"];
Class controllerClass = NSClassFromString(controllerClazz);
// check the controller class...
UIViewController *controller = [[controllerClass alloc] init];
// initialize parameters...
[topVC pushViewController:controller animated:YES];

這種做法的好處是足夠簡單肄梨,但是規(guī)則太過于死板,無法根據(jù)業(yè)務(wù)做定制挠锥。
一種改進的方案是做一個分發(fā)器众羡,建立ID到展示界面方法的映射。

路由方案二

@implement RouterDispatcher

- (void)dispatchMain {
    // 定制
    [topVC pushViewController:mainViewController animated:YES];
}

- (void)dispatchDetail {
    // 定制
    [topVC pushViewController:detailViewController animated:YES];
}

@end

這個方案增加了一層中轉(zhuǎn)蓖租,方便業(yè)務(wù)的定制粱侣,不過缺點也是明顯的羊壹,大量的代碼都堆砌在了 RouterDispatcher 類里。這意味著每次新增界面或者修改業(yè)務(wù)都需要改動到這個類齐婴,顯然作為一個底層核心庫油猫,我們希望最大限度地剝離業(yè)務(wù)以避免改動與保障穩(wěn)定性。如何去掉這個中心化柠偶,就利用到了我們前文所提的“微服務(wù)”思想情妖。

微服務(wù)路由

作為底層框架,我們不想關(guān)心一個ID具體是如何被路由的诱担,我們只提供這種分發(fā)能力毡证,具體的業(yè)務(wù)通過上層注冊來實現(xiàn)。以下面的代碼為例:

@implement RouterDispatcher

static NSMutableDictionary<NSString */*page*/, NSString */*dispatcherClazz*/> kPages;

+ (void)registerPage:(NSString *)pageID {
    if (kPages == nil) {
        kPages = [[NSMutableDictionary alloc] init];
    }
    kPages[page] = NSStringFromClass([self class]);
}

+ (void)dispatchPage:(NSString *)pageID {
    NSString *dispatcherClazz = kPages[pageID];
    if (dispatcherClazz == nil) {
        return;
    }
    Class dispatcherClass = NSClassFromString(dispatcherClazz);
    RouterDispatcher dispatcher = [[dispatcherClass alloc] init];
    [dispatcher dispatch];
}

@end

我們只提供了 registerPage 注冊方法以及 dispatchPage 分發(fā)方法该肴。dispatchPage方法的實現(xiàn)很簡單:根據(jù)已注冊的分發(fā)器做轉(zhuǎn)發(fā)情竹。

那么如何以“微服務(wù)”的形式做注冊呢?

我們注意到了 NSObjectload 方法匀哄,這個方法會在類被加載的時候執(zhí)行秦效,顯然用來做服務(wù)注冊是再合適不過了——類被加載了意味著這個類可用,與此同時注冊上服務(wù)意味著服務(wù)也是可用的涎嚼。這也符合“微服務(wù)”啟動自注冊的理念阱州。

終上,要完成對 "main" 的路由法梯,只需要以下 3 個步驟:

  1. 繼承 RouterDispatcher 實現(xiàn) MainRouterDispatcher
  2. 使用 load 來注冊 "main" 服務(wù)
  3. 實現(xiàn) dispatch 完成路由分發(fā)
@implement MainRouterDispatcher

+ (void)load {
    [self registerPage:@"main"];
}

- (void)dispatch {
    // 業(yè)務(wù)邏輯
    [topVC pushViewController:mainViewController animated:YES];
}

@end

好了苔货,現(xiàn)在底層框架基本OK了,但是對于一線開發(fā)來說立哑,重復(fù)的寫 + (void) load 顯然是件很啰嗦的事夜惭,而且看上去不夠醒目,容易被忽略铛绰。那么這個時候就是“注解”一展身手的時候了诈茧。

使用注解

最終的效果是:


@page(@"main")
- (void)dispatch {
    // Do stuff...
}

首先,注解的基本格式為 @annotation(attr)捂掰,官方常見的一些 @ 打頭的關(guān)鍵字包括:

  • @property
  • @synthesize
  • @dynamic
  • @interface
  • @implement

我們可以使用宏來做替換敢会,比如定義了:

#define my_property property

就可以使用 @my_property 來替代 @property,達到一樣的效果这嚣。

但是這里有一個前提鸥昏,我們選用的 @xx 需要在 @implement @end 區(qū)間內(nèi)部來使用,比較符合的是 @synthesize 姐帚、@dynamic吏垮,但缺點是其后面必須帶上一個屬性,比如 @synthesize title; ,如果當(dāng)前類沒有屬性就無法定義惫皱。

這個時候像樊,我們注意到了一個不常用的 @compatibility_alias。這個注解是用于類名兼容的旅敷,一般開發(fā)不會用到。不過在框架開發(fā)中可能派上用場颤霎。

講到這里媳谁,順便提下我在開發(fā) Pbind 過程中的一個小插曲。當(dāng)時Pbind內(nèi)部實現(xiàn)了一個類 PBRequest 用來統(tǒng)一封裝API請求友酱,突然有一天發(fā)現(xiàn) Apple 的私有庫 ProtocolBuffer.framework 也有這么一個同名的類晴音,控制臺輸出警告:“類名沖突,系統(tǒng)會選擇其中一個而忽略另一個”缔杉。這就尷尬了锤躁,誰知道你哪天選哪個呢?保險起見或详,只能自己換掉系羞,初步的想法是用 _ PBRequest 替換 PBRequest,但是回頭想想這個類是面向開發(fā)者的霸琴,我一大波的 PB 打頭類椒振,突然碰上一個 _ PB 打頭的不是很奇怪么?偶然間發(fā)現(xiàn)了 @compatibility_alias 神器梧乘,兩步即可搞定:

  1. 修改 PBRequest 為 _PBRequest (.h 跟 .m文件)
  2. 在修改后的 _PBRequest.h 文件中加上一句:
    @compatibility_alias PBRequest _PBRequest;

于是澎迎,其他的 所有 引用到 PBRequest 的地方都不需要改動,甚至接入這個庫的使用者依然可以直接使用 PBRequest选调,因為大家都處于同一個編譯環(huán)境下夹供。

OK,回到我們的話題上來仁堪,我們來使用這個神奇的 @compatibility_alias 完成我們的“注解”:

#define page(_pageID_) \
compatibility_alias _RouterDispatcher RouterDispatcher; \
+ (void)load { \
    [self registerPage:_pageID_]; \
}

通過上述定義哮洽,使用 @page(@"main") 時將會被展開:

@compatibility_alias _RouterDispatcher RouterDispatcher; 
+ (void)load { 
    [self registerPage:@"main"]; 
}

相比我們最初給出的代碼,這里唯一添加的一句“廢話”就是:@compatibility_alias _RouterDispatcher RouterDispatcher; 即允許你在當(dāng)前環(huán)境下使用 _“RouterDispatcher” 類枝笨,顯然你不會用到它袁铐。不過我們也不需要過多地關(guān)注它,這句代碼是在編譯時做的横浑,并不會影響到運行時剔桨。

OK,現(xiàn)在我們可以很方便地使用這個注解在任何地方徙融,完成各個界面的路由洒缀。更重要的是 load 方法是系統(tǒng)加載類時自動觸發(fā)的,這意味著你可以把分發(fā)器實現(xiàn)在各個地方,包括你所實施的組件化的某一個 Library 或者 Framework 里树绩。

總結(jié)

本文介紹了“微服務(wù)”的基本思想萨脑,通過 load 方法實現(xiàn)了 iOS 微服務(wù)組件的自注冊。再結(jié)合宏與 @compatibility_alias 完成了 iOS 的“注解”功能饺饭。達到“注解”實現(xiàn)“微服務(wù)”路由的效果:


@page(@"main")
- (void)dispatch {
    // Do stuff...
}

Pbind 是一個支持 LiveLoad 的高度可配置化框架渤早,以上代碼實踐均源自 Pbind 的開發(fā)過程,關(guān)于注解注冊服務(wù)的部分還可以參考 Pbind 源碼中的 PBAction 以及 PBClient 的實現(xiàn)瘫俊。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末鹊杖,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子扛芽,更是在濱河造成了極大的恐慌骂蓖,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件川尖,死亡現(xiàn)場離奇詭異登下,居然都是意外死亡,警方通過查閱死者的電腦和手機叮喳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進店門被芳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人嘲更,你說我怎么就攤上這事筐钟。” “怎么了赋朦?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵篓冲,是天一觀的道長。 經(jīng)常有香客問我宠哄,道長壹将,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任毛嫉,我火速辦了婚禮诽俯,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘承粤。我一直安慰自己暴区,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布辛臊。 她就那樣靜靜地躺著仙粱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪彻舰。 梳的紋絲不亂的頭發(fā)上伐割,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天候味,我揣著相機與錄音,去河邊找鬼隔心。 笑死白群,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的硬霍。 我是一名探鬼主播帜慢,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼须尚!你這毒婦竟也來了崖堤?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤耐床,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后楔脯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體撩轰,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年昧廷,在試婚紗的時候發(fā)現(xiàn)自己被綠了堪嫂。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡木柬,死狀恐怖皆串,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情眉枕,我是刑警寧澤恶复,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站速挑,受9級特大地震影響谤牡,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜姥宝,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一翅萤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧腊满,春花似錦套么、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至疮蹦,卻和暖如春诸迟,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工阵苇, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留壁公,地道東北人。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓绅项,卻偏偏與公主長得像紊册,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子快耿,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,627評論 2 350

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理囊陡,服務(wù)發(fā)現(xiàn),斷路器掀亥,智...
    卡卡羅2017閱讀 134,637評論 18 139
  • 1. 微服務(wù)架構(gòu)介紹 1.1 什么是微服務(wù)架構(gòu)撞反? 形像一點來說,微服務(wù)架構(gòu)就像搭積木搪花,每個微服務(wù)都是一個零件遏片,并使...
    靜修佛緣閱讀 6,635評論 0 39
  • 微服務(wù)最近非常流行,各大互聯(lián)網(wǎng)公司紛紛采用微服務(wù)架構(gòu)體系撮竿,微服務(wù)架構(gòu)模式正在為敏捷部署以及復(fù)雜企業(yè)應(yīng)用實施提供巨大...
    Sting閱讀 9,069評論 0 57
  • 我們的人生吮便,有多少自由可言。 父母一輩的人幢踏,用功讀書的人髓需,成了公務(wù)員;頭腦靈活的人房蝉,成了企業(yè)家僚匆;踏實肯干的人,成了...
    普通先生閱讀 636評論 2 0
  • 毫無疑問惨驶,現(xiàn)在對于人們來說白热,和手機一樣必不可少的,便是裝在手機里面的微信了粗卜。微信的普及屋确,讓博客與微博漸漸淡出了我們...
    默默huangjuan閱讀 704評論 19 7