iOS中消息通知的另一種方式

原理

iOS中消息通知是以notificationName來做為標(biāo)識(shí)坷牛,發(fā)通知時(shí),監(jiān)聽了同一個(gè)notificationName的實(shí)例會(huì)收到通知溜在。

而這里所說的方式是以protocol為標(biāo)識(shí)陌知,發(fā)通知時(shí),以protocol+selector掖肋,來判斷要發(fā)送給哪些實(shí)例仆葡。

做法如下:

  1. 將需要通知的方法定義成protocol,通常是將一組有關(guān)聯(lián)的方法定義在同個(gè)protocol中志笼。

  2. 以protocol為key沿盅,監(jiān)聽該protocol的實(shí)例(可能有多個(gè))為value,組成映射關(guān)系纫溃,即protocol->observer list腰涧。

  3. 防止強(qiáng)引用實(shí)例,使用包裝對(duì)象紊浩,用weak屬性存儲(chǔ)該實(shí)例窖铡。

  4. 發(fā)送通知時(shí),根據(jù)protocol+selector坊谁,拿到對(duì)應(yīng)的實(shí)例费彼,進(jìn)行調(diào)用,同時(shí)傳遞參數(shù)呜袁。

看圖會(huì)比較清晰:

1.png

相比自帶的消息通知敌买,好處是:

  • 自帶的消息通知,添加同一個(gè)notificationName多次阶界,會(huì)收到多個(gè)通知虹钮,而這種方式會(huì)去重聋庵。
  • 參數(shù)靈活,可直接傳遞芙粱,不用包裝成dict和解dict祭玉。
  • 避免移除系統(tǒng)通知的問題
  • 實(shí)例對(duì)象以weak修飾,其釋放后春畔,會(huì)自動(dòng)置為nil脱货。避免野指針問題。

添加觀察者

// 可在內(nèi)部判斷該protocol對(duì)應(yīng)的object是否重復(fù)添加
#define ADD_SERVICE_CLIENT(protocolName, object) 
[[NotificationServiceCenter defaultCenter] addServiceClient:object withKey:@protocol(protocolName)]

添加時(shí)律姨,需帶上要監(jiān)聽的protocol振峻。

發(fā)送通知

#define NOTIFY_SERVICE_CLIENT(protocolName, selector, func) \
{ \
NSArray *__clients__ = [[NotificationServiceCenter defaultCenter] serviceClientsWithKey:@protocol(protocolName)]; \
for (NotificationClient *client in __clients__) \
{ \
id obj = client.object; \
if ([obj respondsToSelector:selector]) \
{ \
[obj func]; \
} \
} \

發(fā)送時(shí)需要加上selector和func。selector用來判斷實(shí)例是否實(shí)現(xiàn)了該selector择份,func用來直接調(diào)用方法扣孟。

if ([obj respondsToSelector:selector])
{ 
    [obj func]; 
} 

Example:

NOTIFY_SERVICE_CLIENT(UserProtocol, @selector(userLogin:), userLogin:19000);

有個(gè)比較巧妙的地方,就是方法的調(diào)用和參數(shù)的傳遞荣赶。使用宏替換的特性凤价,直接寫成[obj func],在編譯時(shí)不會(huì)報(bào)錯(cuò)拔创。比如上面的例子中利诺,func指userLogin:19000,展開調(diào)用就是[obj userLogin:900]剩燥。如果我們要將NOTIFY_SERVICE_CLIENT寫成方法慢逾,是會(huì)編譯不過的,因?yàn)檫@里的func根本就不是一種類型躏吊。所以采用宏氛改,比較tricky的解決這個(gè)問題。

另外比伏,如果在處理通知過程中,又調(diào)用NOTIFY_SERVICE_CLIENT給同一個(gè)protocol+selector發(fā)送通知消息疆导,是會(huì)引起死循環(huán)的赁项。

比如A監(jiān)聽了ProtocolA的test方法,在test方法中調(diào)用NOTIFY_SERVICE_CLIENT(ProtocolA, @selector(test), test)澈段,A又會(huì)收到通知悠菜,循環(huán)往復(fù),就會(huì)造成死循環(huán)败富。所以悔醋,需要注意這種情況。

// 在A中
- (void)addClient {
     ADD_SERVICE_CLIENT(ProtocolA, self)
}

#pragma mark - ProtocolA

// 收到通知
- (void)test {
    NSLog("receive ProtocolA test");
    
    // 會(huì)引起死循環(huán)
    NOTIFY_SERVICE_CLIENT(ProtocolA, @selector(test), test)
}

移除觀察者

#define REMOVE_SERVICE_CLIENT(protocolName, object) 
[[NotificationServiceCenter defaultCenter] removeServiceClient:object withKey:@protocol(protocolName)]

#define REMOVE_ALL_SERVICE_CLIENT(object) 
[[NotificationServiceCenter defaultCenter] removeServiceClient:object]

移除有兩種方式:移除單個(gè)protocol和移除該object監(jiān)聽的所有protocol兽叮。

使用

定義protocol

@protocol UserProtocol <NSObject>

@optional
- (void)logout;

- (void)userLogin:(NSUInteger)userID;

@end

添加監(jiān)聽芬骄,實(shí)現(xiàn)通知的方法猾愿,移除監(jiān)聽。注意需讓其遵循協(xié)議账阻,因?yàn)樵贏DD_SERVICE_CLIENT會(huì)判斷object是否遵循了protocol蒂秘。

@interface ViewController ()<UserProtocol>

@end

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
        
    // 添加監(jiān)聽    
    ADD_SERVICE_CLIENT(UserProtocol, self);
}

- (void)dealloc {
    // 移除監(jiān)聽
    REMOVE_ALL_SERVICE_CLIENT(self);
}

#pragma mark - UserProtocol

// 通知過來后的具體實(shí)現(xiàn)
- (void)logout {
    NSLog(@"logout");
}

- (void)userLogin:(NSUInteger)userId {
    NSLog(@"loginSuccess");
}

發(fā)送通知:

- (IBAction)login:(id)sender {
    NOTIFY_SERVICE_CLIENT(UserProtocol, @selector(userLogin:), userLogin:19000);
}

- (IBAction)logout:(id)sender {
    NOTIFY_SERVICE_CLIENT(UserProtocol, @selector(logout), logout);
}

如果只關(guān)心protocol的某個(gè)方法通知,只需要實(shí)現(xiàn)該方法即可淘太,不需要實(shí)現(xiàn)protocol中的所有方法姻僧。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市蒲牧,隨后出現(xiàn)的幾起案子撇贺,更是在濱河造成了極大的恐慌,老刑警劉巖冰抢,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件松嘶,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡晒屎,警方通過查閱死者的電腦和手機(jī)喘蟆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鼓鲁,“玉大人蕴轨,你說我怎么就攤上這事『Э裕” “怎么了橙弱?”我有些...
    開封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)燥狰。 經(jīng)常有香客問我棘脐,道長(zhǎng),這世上最難降的妖魔是什么龙致? 我笑而不...
    開封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任蛀缝,我火速辦了婚禮,結(jié)果婚禮上目代,老公的妹妹穿的比我還像新娘屈梁。我一直安慰自己,他們只是感情好榛了,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開白布在讶。 她就那樣靜靜地躺著,像睡著了一般霜大。 火紅的嫁衣襯著肌膚如雪构哺。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天战坤,我揣著相機(jī)與錄音曙强,去河邊找鬼残拐。 笑死,一個(gè)胖子當(dāng)著我的面吹牛旗扑,可吹牛的內(nèi)容都是我干的蹦骑。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼臀防,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼眠菇!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起袱衷,我...
    開封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤捎废,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后致燥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體登疗,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年嫌蚤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了辐益。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡脱吱,死狀恐怖智政,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情箱蝠,我是刑警寧澤续捂,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站宦搬,受9級(jí)特大地震影響牙瓢,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜间校,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一矾克、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧憔足,春花似錦聂渊、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽欲逃。三九已至找蜜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間稳析,已是汗流浹背洗做。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工弓叛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人诚纸。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓撰筷,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親畦徘。 傳聞我的和親對(duì)象是個(gè)殘疾皇子毕籽,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

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