iOS-細(xì)數(shù)Objective-C中的回調(diào)機(jī)制

一荣回、協(xié)議
協(xié)議主要是提供接口遭贸、或是類(lèi)似C++多重繼承功能,為類(lèi)提供一種修飾機(jī)制心软。協(xié)議不是為回調(diào)而生的壕吹,它應(yīng)該表述一組互操作約定。
優(yōu)點(diǎn):
實(shí)現(xiàn)簡(jiǎn)單删铃,容易理解耳贬。
強(qiáng)類(lèi)型檢查。

缺點(diǎn):
類(lèi)與類(lèi)間建立了比較強(qiáng)的耦合關(guān)系
有可能需要較長(zhǎng)期保存委托以進(jìn)行回調(diào)猎唁。如果保留的委托需要有獨(dú)占性效拭,可能會(huì)給單件模式、以及多線(xiàn)程帶來(lái)麻煩。
類(lèi)只能通過(guò)一個(gè)方法完成一種類(lèi)型的回調(diào)缎患。代碼邏輯很容易集中到一個(gè)方法中慕的。
大部分回調(diào)使用實(shí)際無(wú)需通過(guò)協(xié)議暴露給外部。

二挤渔、使用respondsToSelector和performSelector進(jìn)行回調(diào)肮街。
利用OBJC的運(yùn)行時(shí)特性,查找對(duì)象的消息進(jìn)行回調(diào)
優(yōu)點(diǎn):
與OBJC代碼兼容性好判导。
具有延遲執(zhí)行等特性嫉父。
輕量級(jí)的回調(diào)機(jī)制。

缺點(diǎn):
回調(diào)產(chǎn)生的返回值只能為id類(lèi)型眼刃,int等類(lèi)型會(huì)產(chǎn)生錯(cuò)誤绕辖。
參數(shù)最多只能傳入兩個(gè)。但可以通過(guò)建立包含多個(gè)參數(shù)的參數(shù)類(lèi)進(jìn)行回避擂红。同時(shí)返回值限制也可通過(guò)此方式解決仪际,即建立一個(gè)輸入類(lèi)和一個(gè)輸出類(lèi)。NSInvocation也提供了多參數(shù)的解決方法昵骤。

如果以
[target performSelector: @selector(callback)];
方式建立回調(diào)树碱,則需要對(duì)類(lèi)的回調(diào)消息名建立約定,且回調(diào)消息名具有獨(dú)占性变秦,即一個(gè)類(lèi)中只能以此消息名進(jìn)行回調(diào)成榜。
如果通過(guò)外部傳入SEL建立回調(diào)
[target performSelector: sel];
或是外部傳入字符串建立回調(diào)
[target performSelector:NSSelectorFromString(@"callback")];
使用自動(dòng)引數(shù)編譯器特征(ARC)會(huì)產(chǎn)生警告“performSelector may cause a leak because its selector is unknown”
使用此種方式建立回調(diào),當(dāng)傳入一個(gè)不符合約定的消息時(shí)蹦玫,會(huì)產(chǎn)生副作用繼續(xù)運(yùn)行赎婚,而非報(bào)錯(cuò)。比如約定消息有2個(gè)參數(shù)樱溉,但傳入消息只有1個(gè)參數(shù)惑淳,則按照參數(shù)約定順序屏蔽掉最后傳入的參數(shù)〗攘或是傳入消息具有3個(gè)參數(shù)歧焦,則多余的參數(shù)值未初始化。

三肚医、函數(shù)指針
傳統(tǒng)的C語(yǔ)言回調(diào)機(jī)制绢馍。
優(yōu)點(diǎn):
輕量級(jí)的回調(diào)機(jī)制。
只約定返回值和參數(shù)肠套,而非函數(shù)名舰涌。無(wú)參數(shù)、返回值限制你稚,使用靈活瓷耙。
編譯器提供類(lèi)型檢查朱躺。(錯(cuò)誤時(shí)產(chǎn)生警告)

缺點(diǎn):
與OBJC的消息機(jī)制不兼容。因?yàn)橄⒉⒎荂語(yǔ)言中那樣搁痛,函數(shù)名對(duì)應(yīng)函數(shù)指針长搀。即只能對(duì)C函數(shù)進(jìn)行回調(diào)。
傳入不符合約定的函數(shù)指針時(shí)鸡典,產(chǎn)生副作用繼續(xù)運(yùn)行源请,而非報(bào)錯(cuò)。

四彻况、objc_msgSend
通過(guò)導(dǎo)入#import <objc/message.h>獲得運(yùn)行時(shí)的消息調(diào)用谁尸。
其定義為
id objc_msgSend(id theReceiver, SEL theSelector, ...)
優(yōu)點(diǎn):
輕量級(jí)的回調(diào)機(jī)制。
無(wú)傳入?yún)?shù)限制纽甘。
相比performSelector良蛮,使用自動(dòng)引數(shù)特征時(shí),不產(chǎn)生警告悍赢。
同系列的方法支持double决瞳、struct等類(lèi)型的返回值,但仍然不支持int型返回值(可使用NSNumber包裝以回避)泽裳。

缺點(diǎn):
傳入不符合約定的消息時(shí)瞒斩,產(chǎn)生副作用繼續(xù)運(yùn)行破婆,而非報(bào)錯(cuò)涮总。

五、IMP
IMP類(lèi)似于OBJC提供的函數(shù)指針祷舀,它通過(guò)methodForSelector方法查詢(xún)傳入的Selector瀑梗,以獲得函數(shù)的入口地址。
其定義為
id (*IMP)(id, SEL, ...)
相比普通C語(yǔ)言的函數(shù)指針裳扯,其定義多了id,SEL這兩個(gè)強(qiáng)制參數(shù)約定抛丽,其他與函數(shù)指針無(wú)異。
優(yōu)點(diǎn):
輕量級(jí)的回調(diào)機(jī)制饰豺。
傳入不符合約定的消息時(shí)亿鲜,報(bào)錯(cuò)。
無(wú)傳入?yún)?shù)限制冤吨。返回值可通過(guò)強(qiáng)轉(zhuǎn)獲得蒿柳,無(wú)類(lèi)型限制。如:

      typedef int (*CBFUNC)(id, SEL, int, int, int); // 定義函數(shù)指針類(lèi)型
      int ret = ((CBFUNC)callback)(self, sel, param1, param2, param3); // 強(qiáng)制轉(zhuǎn)換

這里的id和SEL只是OBJC系統(tǒng)約定的占位漩蟆,自定義回調(diào)時(shí)無(wú)實(shí)際意義垒探。
由于此階段實(shí)際是函數(shù)指針調(diào)用,因此最好還是typedef定義函數(shù)指針怠李,然后對(duì)IMP強(qiáng)轉(zhuǎn)一下圾叼,以免出現(xiàn)錯(cuò)誤蛤克,也能提供一些編譯期保護(hù)。

缺點(diǎn):
依然不能提供如同協(xié)議和函數(shù)指針的編譯期類(lèi)型檢查

六夷蚊、NSNotificationCenter
NSNotificationCenter是OBJC提供的消息機(jī)制构挤。它有些類(lèi)似于觀(guān)察者模式,通過(guò)關(guān)注感興趣的消息撬码,建立回調(diào)儿倒。NSNotificationCenter提供了一種低耦合的對(duì)象通訊機(jī)制,特別適合無(wú)指定對(duì)象的一對(duì)多回調(diào)呜笑。
主要方法:
1)獲取消息中心實(shí)例(系統(tǒng)已創(chuàng)建夫否,單件模式)
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
2)發(fā)送消息。(事件發(fā)生時(shí)調(diào)用)

    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
    [nc postNotificationName: NOTIFY_MSG_UC_COMMON_PLAYER_PLAY   // 消息名(字符串)
                      object:self                                // 消息源
                    userInfo:nil];                               // 用戶(hù)字典(傳遞更多自定義參數(shù))

3)注冊(cè)消息

    [nc addObserver: self                              // 觀(guān)察者
           selector: @selector(handleNotify_Play:)     // 回調(diào)
               name: NOTIFY_MSG_UC_COMMON_PLAYER_PLAY  // 監(jiān)聽(tīng)消息
             object: nil];                             // 消息源
```
4)注銷(xiāo)消息
```
    [nc removeObserver: self];
```
5)回調(diào)定義
```
    - (void) handleNotify_Play:(NSNotification *)note;
```
   只有一個(gè)參數(shù)
     NSNotification*
     –name      // 消息名
     –object    // 消息源
     –userInfo  // 用戶(hù)字典

優(yōu)點(diǎn):
回調(diào)對(duì)象間耦合度低叫胁。相互之間可不必知道對(duì)方存在凰慈。
通過(guò)消息傳遞的信息無(wú)限制。
觀(guān)察者可選擇特定消息驼鹅、特定對(duì)象微谓,或者特定對(duì)象的特定消息進(jìn)行觀(guān)察。

缺點(diǎn):
缺乏時(shí)序性输钩。當(dāng)事件發(fā)生時(shí)豺型,回調(diào)執(zhí)行的先后次序不確定。也不能等待回調(diào)完成執(zhí)行后續(xù)操作买乃。解決:1)使用傳統(tǒng)回調(diào)機(jī)制姻氨。2)多線(xiàn)程時(shí),可使用NSCondition同步線(xiàn)程剪验。3)使用更多的消息肴焊。(過(guò)多使用可能導(dǎo)致混亂)

**七、Block**
Block是OBJC提供的一種運(yùn)行時(shí)方法機(jī)制功戚,類(lèi)似于Javascript的匿名函數(shù)娶眷。它提供了一種運(yùn)行時(shí)的臨時(shí)回調(diào)機(jī)制。
Block對(duì)象的聲明:
  聲明一個(gè)參數(shù)為int啸臀,返回值為int的Block對(duì)象cb届宠。
    int (^cb)(int);
  也可以通過(guò)typedef簡(jiǎn)化定義。
    typedef int(^BLOCK_CALLBACK_FUNC)(int);
    BLOCK_CALLBACK_FUNC cb = …
  回調(diào)函數(shù)定義:
```
    -(int)handleBlockCallbackFunc: (BLOCK_CALLBACK_FUNC)callback
    {
      return callback(10);
    }
```
回調(diào)函數(shù)使用:
  1)賦值后使用
```
     BLOCK_CALLBACK_FUNC cb =
       ^(int param)
       {
         NSLog(@"Block Msg: %d", param);
         return param*2;
       };
       int ret = [self handleBlockCallbackFunc:cb];
```
  2)使用時(shí)賦值
```
     int ret = [self handleBlockCallbackFunc:
                 ^(int param) {
                   NSLog(@"Block Msg: %d", param);
                   return param*2;
                 }];
```
注意:
**1)block對(duì)象使用的變量乘粒、參數(shù)在運(yùn)行時(shí)被綁定豌注,因此可以直接使用棧空間建立的變量谓厘,無(wú)需參數(shù)傳入幌羞。但block對(duì)象的創(chuàng)建依然有生命周期限制,因此傳入異步調(diào)用的block對(duì)象時(shí)竟稳,如果是検翳耄空間創(chuàng)建的block熊痴,必須**
**使用Block_copy()將block拷出備份,然后使用Block_release()將block釋放聂宾。參見(jiàn)Using Blocks章果善,Patterns to Avoid節(jié)**
**2)對(duì)于在棧空間聲明的變量系谐,綁定到block時(shí)被標(biāo)記為const巾陕。只能讀取不能寫(xiě)入。如果需要寫(xiě)入纪他,需要用__block對(duì)變量進(jìn)行標(biāo)記鄙煤。此時(shí)block使用的是從棧拷貝到堆中的對(duì)象茶袒。當(dāng)出block時(shí)梯刚,如果棧可用則將堆中對(duì)象自動(dòng)拷貝回棧薪寓。**
優(yōu)點(diǎn):
最輕量級(jí)的回調(diào)機(jī)制亡资。
編譯器類(lèi)型檢查。
如函數(shù)指針一樣向叉,靈活定義回調(diào)函數(shù)锥腻。

缺點(diǎn):
執(zhí)行效率。(影響程度不清楚)
容易導(dǎo)致代碼邏輯集中母谎。
IOS4之后的特性

**總結(jié):**
OBJC還沒(méi)有太完美的輕量級(jí)回調(diào)機(jī)制瘦黑,只能根據(jù)情況選擇合適的機(jī)制。

單純的回調(diào)销睁,且沒(méi)有復(fù)用的必要供璧,也無(wú)IOS版本限制存崖,可采用block冻记。
單純的回調(diào),有復(fù)用要求来惧,可使用performSelector冗栗、objc_msgSend,或是IMP的回調(diào)機(jī)制供搀。
使用自動(dòng)引數(shù)的情況下隅居,盡量不使用performSelector回調(diào)傳入的@Selector,防止警告葛虐。
對(duì)象間有較多的互操作胎源,對(duì)象有復(fù)用的必要,可采用協(xié)議屿脐。
無(wú)指定對(duì)象的一對(duì)多回調(diào)采用NSNotificationCenter涕蚤。
有延遲調(diào)用等特殊應(yīng)用的宪卿,可以使用performSelector。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末万栅,一起剝皮案震驚了整個(gè)濱河市佑钾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌烦粒,老刑警劉巖休溶,帶你破解...
    沈念sama閱讀 212,029評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異扰她,居然都是意外死亡兽掰,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,395評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)徒役,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)禾进,“玉大人,你說(shuō)我怎么就攤上這事廉涕⌒涸疲” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,570評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵狐蜕,是天一觀(guān)的道長(zhǎng)宠纯。 經(jīng)常有香客問(wèn)我,道長(zhǎng)层释,這世上最難降的妖魔是什么婆瓜? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,535評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮贡羔,結(jié)果婚禮上廉白,老公的妹妹穿的比我還像新娘。我一直安慰自己乖寒,他們只是感情好猴蹂,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,650評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著楣嘁,像睡著了一般磅轻。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上逐虚,一...
    開(kāi)封第一講書(shū)人閱讀 49,850評(píng)論 1 290
  • 那天聋溜,我揣著相機(jī)與錄音,去河邊找鬼叭爱。 笑死撮躁,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的买雾。 我是一名探鬼主播把曼,決...
    沈念sama閱讀 39,006評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼缨称,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了祝迂?” 一聲冷哼從身側(cè)響起睦尽,我...
    開(kāi)封第一講書(shū)人閱讀 37,747評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎型雳,沒(méi)想到半個(gè)月后当凡,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,207評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡纠俭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,536評(píng)論 2 327
  • 正文 我和宋清朗相戀三年沿量,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片冤荆。...
    茶點(diǎn)故事閱讀 38,683評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡朴则,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出钓简,到底是詐尸還是另有隱情乌妒,我是刑警寧澤,帶...
    沈念sama閱讀 34,342評(píng)論 4 330
  • 正文 年R本政府宣布外邓,位于F島的核電站撤蚊,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏损话。R本人自食惡果不足惜侦啸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,964評(píng)論 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望丧枪。 院中可真熱鬧光涂,春花似錦、人聲如沸拧烦。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,772評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)屎篱。三九已至服赎,卻和暖如春葵蒂,著一層夾襖步出監(jiān)牢的瞬間交播,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,004評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工践付, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留秦士,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,401評(píng)論 2 360
  • 正文 我出身青樓永高,卻偏偏與公主長(zhǎng)得像隧土,于是被迫代替她去往敵國(guó)和親提针。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,566評(píng)論 2 349

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