NNPopObjc:在 Objective-C 上進(jìn)行面向協(xié)議的編程(上)

本文將介紹基于 NNPopObjc 在 Objective-C 上進(jìn)行面向協(xié)議的編程殖氏。因?yàn)槿績(jī)?nèi)容比較長(zhǎng),所以分成了上下兩個(gè)部分弄诲,本文 (上) 主要介紹了 NNPopObjc 的使用澎蛛,包括默認(rèn)協(xié)議擴(kuò)展、約束協(xié)議擴(kuò)展等躏鱼。下半部分氮采,用于介紹 NNPopObjc 的實(shí)現(xiàn)思路和原理。

引子

基于 Swift 特性染苛,Apple 在 2015 年 WWDC 上提出了面向協(xié)議編程 (Protocol Oriented Programming鹊漠,以下簡(jiǎn)稱 POP)的編程范式。相比與傳統(tǒng)的面向?qū)ο缶幊?(OOP)茶行,POP 顯得更加靈活躯概。但是由于 Objective-C 缺失協(xié)議擴(kuò)展能力,導(dǎo)致 Objective-C 無(wú)法基于 POP 范式進(jìn)行項(xiàng)目開發(fā)畔师。NNPopObjc 用于為 Objective-C 提供協(xié)議擴(kuò)展能力楞陷,從而實(shí)現(xiàn) Objective-C 的面向協(xié)議編程。

協(xié)議擴(kuò)展

通過(guò)協(xié)議擴(kuò)展茉唉,可以為協(xié)議提供類方法、對(duì)象方法以及計(jì)算屬性的默認(rèn)實(shí)現(xiàn)结执。如果遵循該協(xié)議的類提供了自己所需方法或?qū)傩苑椒ǖ膶?shí)現(xiàn)度陆,則將使用該類的實(shí)現(xiàn),不使用擴(kuò)展提供的實(shí)現(xiàn)献幔。

協(xié)議定義中方法申明分為:@required@optional 懂傀,由于 @required 聲明的方法必須由遵守協(xié)議的類進(jìn)行實(shí)現(xiàn),所以由 @required 申明的方法一定會(huì)使用當(dāng)前類的實(shí)現(xiàn)蜡感。

NNPopObjc 提供兩個(gè)宏函數(shù)關(guān)鍵字:

  • @nn_extension(...)
  • @nn_where(...)

默認(rèn)協(xié)議擴(kuò)展

實(shí)現(xiàn)一個(gè)協(xié)議擴(kuò)展蹬蚁,不對(duì)擴(kuò)展進(jìn)行任何約束,那么此擴(kuò)展被認(rèn)為是該協(xié)議的默認(rèn)擴(kuò)展郑兴。

聲明一個(gè) NNCodeProtocol 協(xié)議

@protocol NNCodeProtocol <NSObject>

@optional
+ (void)sayHelloPop;
- (void)sayHelloPop;

@end

為 NNCodeProtocol 協(xié)議提供默認(rèn)實(shí)現(xiàn)

@nn_extension(NNCodeProtocol)

+ (void)sayHelloPop {
    DLog(@"+[%@ %s] code says hello pop", self, sel_getName(_cmd));
}

- (void)sayHelloPop {
    DLog(@"-[%@ %s] code says hello pop", [self class], sel_getName(_cmd));
}

@end

定義 NNCodeC 遵守 NNCodeProtocol 協(xié)議

// 類聲明
@interface NNCodeC : NSObject <NNCodeProtocol>

@end
// 類實(shí)現(xiàn)
@implementation NNCodeC

@end

方法調(diào)用

[NNCodeC sayHelloPop];
[[NNCodeC new] sayHelloPop];

輸出結(jié)果

+[NNCodeC sayHelloPop] code says hello pop
-[NNCodeC sayHelloPop] code says hello pop

約束協(xié)議擴(kuò)展

@nn_where 關(guān)鍵字

NNPopObjc 提供 @nn_where 關(guān)鍵字為協(xié)議擴(kuò)展提供約束犀斋,遵守協(xié)議的類需要滿足約束才能夠使用協(xié)議擴(kuò)展提供的方法實(shí)現(xiàn)。

下面示例中通過(guò) @nn_where 為 NNCodeProtocol 擴(kuò)展添加了約束情连,約束要求遵守協(xié)議的類必須是 NNCodeObjc叽粹,其中 self 即當(dāng)前要遵守協(xié)議的類。

@nn_extension(NNCodeProtocol, @nn_where(self == [NNCodeObjc class]))

+ (void)sayHelloPop {
    DLog(@"+[%@ %s] objc says hello pop", self, sel_getName(_cmd));
}

- (void)sayHelloPop {
    DLog(@"-[%@ %s] objc says hello pop", [self class], sel_getName(_cmd));
}

@end

方法調(diào)用

[NNCodeC sayHelloPop];
[[NNCodeC new] sayHelloPop];
[NNCodeObjc sayHelloPop];
[[NNCodeObjc new] sayHelloPop];

NNCodeObjc 優(yōu)先使用滿足約束協(xié)議的實(shí)現(xiàn),輸出結(jié)果

+[NNCodeC sayHelloPop] code says hello pop
-[NNCodeC sayHelloPop] code says hello pop
+[NNCodeObjc sayHelloPop] objc says hello pop
-[NNCodeObjc sayHelloPop] objc says hello pop

類遵守協(xié)議列表

在約束協(xié)議擴(kuò)展的 @nn_where 關(guān)鍵字后虫几,可以跟隨多個(gè)其他協(xié)議锤灿。遵守協(xié)議的類也必須同時(shí)要遵守該列表中所有的協(xié)議。

協(xié)議擴(kuò)展增加協(xié)議列表后辆脸,在協(xié)議擴(kuò)展中可以訪問(wèn)協(xié)議列表聲明的方法但校,使協(xié)議擴(kuò)展變的更加靈活。

定義一個(gè)協(xié)議

@protocol NNCodeNameProtocol <NSObject>

@optional
@property (nonatomic, strong) NSString* name;

@end

修改 NNCodeC 遵守該協(xié)議啡氢,并實(shí)現(xiàn)協(xié)議屬性

@interface NNCodeC : NSObject < NNCodeProtocol, NNCodeNameProtocol>

@property (nonatomic, strong) NSString *name;

@end

修改 NNCodeProtocol 的約束協(xié)議擴(kuò)展状囱,訪問(wèn) name 屬性。

@nn_extension(NNCodeProtocol, @nn_where(), NNCodeNameProtocol)

+ (void)sayHelloPop {
    DLog(@"+[%@ %s] code says hello pop", self, sel_getName(_cmd));
}

- (void)sayHelloPop {
    DLog(@"-[%@ %s] code says hello pop, i am %@ ", [self class], sel_getName(_cmd), self.name);
}

@end

方法調(diào)用

[NNCodeC sayHelloPop];
NNCodeC *objc = [NNCodeC new];
objc.who = @"c";
[objc sayHelloPop];

輸出結(jié)果

+[NNCodeC sayHelloPop] code says hello pop
-[NNCodeC sayHelloPop] code says hello pop, i am c

協(xié)議繼承

在 NNCodeObjc 中擴(kuò)展支持協(xié)議繼承空执。

聲明一個(gè)協(xié)議 NNCodeWhoProtocol 繼承自 NNCodeProtocol

@protocol NNCodeWhoProtocol <NNCodeProtocol>

@optional
@property (nonatomic, strong) NSString* who;

@end

為 NNCodeWhoProtocol 協(xié)議提供擴(kuò)展

@nn_extension(NNCodeWhoProtocol, @nn_where(), NNCodeNameProtocol)

- (void)sayHelloPop {
    DLog(@"-[%@ %s] code says hello pop", [self class], sel_getName(_cmd));
}

- (NSString *)who {
    NSString *who = [NSString stringWithFormat:@"-[%@ %s] code says I am %@", [self class], sel_getName(_cmd), self.name];
    return who;
}

- (void)setWho:(NSString *)who {
    self.name = who;
}

@end

方法調(diào)用

[NNCodeC sayHelloPop];
NNCodeC *objc = [NNCodeC new];
objc.who = @"c";
[objc sayHelloPop];
DLog(@"%@", c.who);

輸出結(jié)果

+[NNCodeC sayHelloPop] code says hello pop
-[NNCodeC sayHelloPop] code says hello pop
-[NNCodeC who] code says I am c

同一協(xié)議多個(gè)擴(kuò)展的問(wèn)題

協(xié)議的擴(kuò)展分為兩類:

  • 默認(rèn)協(xié)議擴(kuò)展
  • 約束協(xié)議擴(kuò)展

同一協(xié)議可以多個(gè)擴(kuò)展浪箭,但在遵守協(xié)議類的角度,一個(gè)協(xié)議的默認(rèn)協(xié)議擴(kuò)展和約束協(xié)議擴(kuò)展分別不能為多個(gè)辨绊,(即 默認(rèn)協(xié)議擴(kuò)展的個(gè)數(shù) <= 1 && 約束協(xié)議擴(kuò)展的個(gè)數(shù) <= 1)奶栖,如果存在多個(gè)就會(huì)產(chǎn)生所謂的“菱形缺陷”問(wèn)題,程序執(zhí)行時(shí)會(huì)觸發(fā) NNPopObjc assert 斷言门坷。

一個(gè)使用示例

ProtocolNetwork喵神王巍 在 MDCC 16 (移動(dòng)開發(fā)者大會(huì)) iOS 專場(chǎng)《面向協(xié)議編程與 Cocoa 的邂逅》主題演講時(shí)使用的 Demo 工程宣鄙, Demo 演示了 Swift 面向協(xié)議編程在 Cocoa 中的使用示例。

ProtocolNetworkObjc 是基于 NNPopObjc 完全復(fù)刻的 Objective-C 版 ProtocolNetwork 默蚌。

由于 ProtocolNetworkObjc 完全復(fù)刻 ProtocolNetwork冻晤,具體代碼演變及分析可參考:喵神王巍的博客 面向協(xié)議編程與 Cocoa 的邂逅 (下) 中相關(guān)內(nèi)容。限于篇幅绸吸,本文不對(duì)代碼進(jìn)行講解鼻弧。

本文下半部分將介紹 NNPopObjc 的實(shí)現(xiàn)思路和原理。


歡迎訪問(wèn)我的GitHub博客

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末锦茁,一起剝皮案震驚了整個(gè)濱河市攘轩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌码俩,老刑警劉巖度帮,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異稿存,居然都是意外死亡笨篷,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門瓣履,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)率翅,“玉大人,你說(shuō)我怎么就攤上這事拂苹“财福” “怎么了痰洒?”我有些...
    開封第一講書人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)浴韭。 經(jīng)常有香客問(wèn)我丘喻,道長(zhǎng),這世上最難降的妖魔是什么念颈? 我笑而不...
    開封第一講書人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任泉粉,我火速辦了婚禮,結(jié)果婚禮上榴芳,老公的妹妹穿的比我還像新娘嗡靡。我一直安慰自己,他們只是感情好窟感,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開白布讨彼。 她就那樣靜靜地躺著,像睡著了一般柿祈。 火紅的嫁衣襯著肌膚如雪哈误。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,754評(píng)論 1 307
  • 那天躏嚎,我揣著相機(jī)與錄音蜜自,去河邊找鬼。 笑死卢佣,一個(gè)胖子當(dāng)著我的面吹牛重荠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播虚茶,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼戈鲁,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了嘹叫?” 一聲冷哼從身側(cè)響起荞彼,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎待笑,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體抓谴,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡暮蹂,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了癌压。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片仰泻。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖滩届,靈堂內(nèi)的尸體忽然破棺而出集侯,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布棠枉,位于F島的核電站浓体,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏辈讶。R本人自食惡果不足惜命浴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一逐抑、第九天 我趴在偏房一處隱蔽的房頂上張望锰蓬。 院中可真熱鬧丸边,春花似錦憋他、人聲如沸贤徒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)员舵。三九已至扯躺,卻和暖如春捉兴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背缅帘。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工轴术, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人钦无。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓逗栽,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親失暂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子彼宠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

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