一荣回、協(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。