之前看的別人的保存的
一、協(xié)議
協(xié)議主要是提供接口、或是類似C++多重繼承功能,為類提供一種修飾機(jī)制。協(xié)議不是為回調(diào)而生的,它應(yīng)該表述一組互操作約定。
優(yōu)點(diǎn):
實(shí)現(xiàn)簡單,容易理解。
強(qiáng)類型檢查。
缺點(diǎn):
類與類間建立了比較強(qiáng)的耦合關(guān)系
有可能需要較長期保存委托以進(jìn)行回調(diào)。如果保留的委托需要有獨(dú)占性,可能會(huì)給單件模式、以及多線程帶來麻煩。
類只能通過一個(gè)方法完成一種類型的回調(diào)。代碼邏輯很容易集中到一個(gè)方法中。
大部分回調(diào)使用實(shí)際無需通過協(xié)議暴露給外部。
二、使用respondsToSelector和performSelector進(jìn)行回調(diào)。
利用OBJC的運(yùn)行時(shí)特性,查找對象的消息進(jìn)行回調(diào)
優(yōu)點(diǎn):
與OBJC代碼兼容性好。
具有延遲執(zhí)行等特性。
輕量級(jí)的回調(diào)機(jī)制。
缺點(diǎn):
回調(diào)產(chǎn)生的返回值只能為id類型,int等類型會(huì)產(chǎn)生錯(cuò)誤。
參數(shù)最多只能傳入兩個(gè)。但可以通過建立包含多個(gè)參數(shù)的參數(shù)類進(jìn)行回避。同時(shí)返回值限制也可通過此方式解決,即建立一個(gè)輸入類和一個(gè)輸出類。NSInvocation也提供了多參數(shù)的解決方法。
如果以
[target performSelector: @selector(callback)];
方式建立回調(diào),則需要對類的回調(diào)消息名建立約定,且回調(diào)消息名具有獨(dú)占性,即一個(gè)類中只能以此消息名進(jìn)行回調(diào)。
如果通過外部傳入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ù)?;蚴莻魅胂⒕哂?個(gè)參數(shù),則多余的參數(shù)值未初始化。
三、函數(shù)指針
傳統(tǒng)的C語言回調(diào)機(jī)制。
優(yōu)點(diǎn):
輕量級(jí)的回調(diào)機(jī)制。
只約定返回值和參數(shù),而非函數(shù)名。無參數(shù)、返回值限制,使用靈活。
編譯器提供類型檢查。(錯(cuò)誤時(shí)產(chǎn)生警告)
缺點(diǎn):
與OBJC的消息機(jī)制不兼容。因?yàn)橄⒉⒎荂語言中那樣,函數(shù)名對應(yīng)函數(shù)指針。即只能對C函數(shù)進(jìn)行回調(diào)。
傳入不符合約定的函數(shù)指針時(shí),產(chǎn)生副作用繼續(xù)運(yùn)行,而非報(bào)錯(cuò)。
四、objc_msgSend
通過導(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ī)制。
無傳入?yún)?shù)限制。
相比performSelector,使用自動(dòng)引數(shù)特征時(shí),不產(chǎn)生警告。
同系列的方法支持double、struct等類型的返回值,但仍然不支持int型返回值(可使用NSNumber包裝以回避)。
缺點(diǎn):
傳入不符合約定的消息時(shí),產(chǎn)生副作用繼續(xù)運(yùn)行,而非報(bào)錯(cuò)。
五、IMP
IMP類似于OBJC提供的函數(shù)指針,它通過methodForSelector方法查詢傳入的Selector,以獲得函數(shù)的入口地址。
其定義為
id (*IMP)(id, SEL, ...)
相比普通C語言的函數(shù)指針,其定義多了id,SEL這兩個(gè)強(qiáng)制參數(shù)約定,其他與函數(shù)指針無異。
優(yōu)點(diǎn):
輕量級(jí)的回調(diào)機(jī)制。
傳入不符合約定的消息時(shí),報(bào)錯(cuò)。
無傳入?yún)?shù)限制。返回值可通過強(qiáng)轉(zhuǎn)獲得,無類型限制。如:
typedef int (*CBFUNC)(id, SEL, int, int, int); // 定義函數(shù)指針類型
int ret = ((CBFUNC)callback)(self, sel, param1, param2, param3); // 強(qiáng)制轉(zhuǎn)換
這里的id和SEL只是OBJC系統(tǒng)約定的占位,自定義回調(diào)時(shí)無實(shí)際意義。
由于此階段實(shí)際是函數(shù)指針調(diào)用,因此最好還是typedef定義函數(shù)指針,然后對IMP強(qiáng)轉(zhuǎn)一下,以免出現(xiàn)錯(cuò)誤,也能提供一些編譯期保護(hù)。
缺點(diǎn):
依然不能提供如同協(xié)議和函數(shù)指針的編譯期類型檢查
六、NSNotificationCenter
NSNotificationCenter是OBJC提供的消息機(jī)制。它有些類似于觀察者模式,通過關(guān)注感興趣的消息,建立回調(diào)。NSNotificationCenter提供了一種低耦合的對象通訊機(jī)制,特別適合無指定對象的一對多回調(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]; // 用戶字典(傳遞更多自定義參數(shù))
3)注冊消息
[nc addObserver: self // 觀察者
selector: @selector(handleNotify_Play:) // 回調(diào)
name: NOTIFY_MSG_UC_COMMON_PLAYER_PLAY // 監(jiān)聽消息
object: nil]; // 消息源
4)注銷消息
[nc removeObserver: self];
5)回調(diào)定義
- (void) handleNotify_Play:(NSNotification *)note;
只有一個(gè)參數(shù)
NSNotification*
–name // 消息名
–object // 消息源
–userInfo // 用戶字典
優(yōu)點(diǎn):
回調(diào)對象間耦合度低。相互之間可不必知道對方存在。
通過消息傳遞的信息無限制。
觀察者可選擇特定消息、特定對象,或者特定對象的特定消息進(jìn)行觀察。
缺點(diǎn):
缺乏時(shí)序性。當(dāng)事件發(fā)生時(shí),回調(diào)執(zhí)行的先后次序不確定。也不能等待回調(diào)完成執(zhí)行后續(xù)操作。解決:1)使用傳統(tǒng)回調(diào)機(jī)制。2)多線程時(shí),可使用NSCondition同步線程。3)使用更多的消息。(過多使用可能導(dǎo)致混亂)
七、Block
Block是OBJC提供的一種運(yùn)行時(shí)方法機(jī)制,類似于Javascript的匿名函數(shù)。它提供了一種運(yùn)行時(shí)的臨時(shí)回調(diào)機(jī)制。
Block對象的聲明:
聲明一個(gè)參數(shù)為int,返回值為int的Block對象cb。
int (^cb)(int);
也可以通過typedef簡化定義。
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對象使用的變量、參數(shù)在運(yùn)行時(shí)被綁定,因此可以直接使用??臻g建立的變量,無需參數(shù)傳入。但block對象的創(chuàng)建依然有生命周期限制,因此傳入異步調(diào)用的block對象時(shí),如果是??臻g創(chuàng)建的block,必須
使用Block_copy()將block拷出備份,然后使用Block_release()將block釋放。參見Using Blocks章,Patterns to Avoid節(jié)
2)對于在??臻g聲明的變量,綁定到block時(shí)被標(biāo)記為const。只能讀取不能寫入。如果需要寫入,需要用__block對變量進(jìn)行標(biāo)記。此時(shí)block使用的是從??截惖蕉阎械膶ο蟆.?dāng)出block時(shí),如果??捎脛t將堆中對象自動(dòng)拷貝回棧。
優(yōu)點(diǎn):
最輕量級(jí)的回調(diào)機(jī)制。
編譯器類型檢查。
如函數(shù)指針一樣,靈活定義回調(diào)函數(shù)。
缺點(diǎn):
執(zhí)行效率。(影響程度不清楚)
容易導(dǎo)致代碼邏輯集中。
IOS4之后的特性
總結(jié):
OBJC還沒有太完美的輕量級(jí)回調(diào)機(jī)制,只能根據(jù)情況選擇合適的機(jī)制。
單純的回調(diào),且沒有復(fù)用的必要,也無IOS版本限制,可采用block。
單純的回調(diào),有復(fù)用要求,可使用performSelector、objc_msgSend,或是IMP的回調(diào)機(jī)制。
使用自動(dòng)引數(shù)的情況下,盡量不使用performSelector回調(diào)傳入的@Selector,防止警告。
對象間有較多的互操作,對象有復(fù)用的必要,可采用協(xié)議。
無指定對象的一對多回調(diào)采用NSNotificationCenter。
有延遲調(diào)用等特殊應(yīng)用的,可以使用performSelector。