第十二章 協議

陳振發表於2017-12-13

協議就是宣告方法的集合,它表示物件的行為。協議與具體的實現無關,它是根據訊息來獲取物件的一套系統化的方法。如果能夠熟練掌握協議的使用,就能夠定義出高靈活性,低耦合性的類。

協議的概念

什麼是協議

表示物件的作用和行為的方法的集合體就稱為協議(prototol)。舉個例子,CD播放器,MD播放器,數字音訊播放器中播放,停止,跳過等公共功能就是我們所說的協議。只要與這些操作方法相對應,也就是說只要提供了公共協議,無論使用什麼樣的儲存媒體,就都可以播放或停止播放音樂。而對協議來說,每個播放器也可以獨立實現各自的功能。

物件的協議

在物件模型化的軟體世界中,不同的物件也可能包含相同的方法集合,但通常情況下,這些物件並不是繼承關係。

OC中的協議僅僅是宣告方法的集合體,實現方法則由各個類自行完成。因此,使用協議的各個類之間是否有繼承關係都無關緊要,重要的是如何實現這些方法。

如果類實現了該協議宣告的所有方法,我們就說類遵循(conform)該協議。而它的子類例項因為繼承關係也擁有了這些協議的方法。當類適用於某個協議時,它的例項也適用於這個協議。

協議也可以被理解成只有宣告而沒有實現方法和例項變數等的抽象類。

Objective-C中協議的宣告

協議的宣告

@protocol協議名

宣告方法;

@end

協議名命名規範於類名相同,即首字母大寫,其他字母小寫。此外,協議名也可以與已有的類同名。方法的宣告中也可以使用屬性宣告。

協議通常被作為標頭檔案書寫,並在類的宣告之前匯入。

協議的採用

@interface類名:超類名<協議名

> {

宣告介面變數;

...

}

宣告方法;

...

@end

類的介面宣告指定了某個協議,我們稱為類採用了該協議。

這樣宣告時,協議中的方法就被作為了類宣告的一部分。因此,在類的介面宣告中,就無須再宣告這些方法了。協議中的方法在實現檔案中也必須要實現。但在超類中已實現的方法就不用再重新實現了。

子類成為該協議適用的類,而子類也可以同時使用別的協議。

一個類中可以同時採用多個協議,在<>括號內,將多個協議名用逗號分隔即可。

即使多個協議中重複包含同一個方法的宣告也沒有問題。

但是,選擇器相同而函式引數和返回值的型別不一樣,即簽名不同的方法在協議中重複宣告時就會出現問題。一個類內不能宣告包含同一個選擇器的另一個方法,也不能定義多個這樣的協議。

而當協議中的方法在某個範疇中實現時,就可以在該範疇中宣告採用該協議:

@interface類名(範疇名)<協議名>宣告方法;

...

@end

通過此方法還可以在已知類中新增協議的方法實現。

協議的繼承

在某個協議中,可以追加另一組方法來產生新的協議,這稱為協議的繼承。宣告方法如下:

@protocol協議名1<協議名2>宣告方法

...

@end

而且協議還可以有多個繼承源,增加多個繼承源時在<>內將多個協議名用逗號分隔即可。

指定協議的型別宣告

宣告某個物件適用於某個協議,例如宣告obj適用於協議S時,就可以採用如下方式定義:

id obj;

該宣告也可以作為臨時引數。

在宣告指定協議的型別時編譯器會對型別進行靜態檢查。但需注意的是,在執行時並不會對型別進行動態檢查。

類是否適用於協議,與每個方法是否得到了實現無關,而是根據在介面檔案中是否宣告瞭採用協議來判斷。

不僅是id型別,具體的類名和範疇的組合也可以被當成型別來使用。如下所示:

- (void)setAlternativeView:](NSView *)aView;

在此例中,引數aView不僅是類NSView的例項,還使用了範疇或繼承,同時還是協議Clickable適用的物件,此例中靜態說明了這些特性。

如果一個物件使用了協議,那麼在指定該物件的類時,類物件只要能適用於指定的協議就行,而不用管它是什麼類的物件。這樣就可以編寫出不依賴於具體的類的實現的,高靈活性的程式碼。

程式碼中只關注協議和抽象類,而沒有具體類名的物件稱為匿名物件(anonymous object)。例如:

[ Person new ];

沒有變數名。

協議的前置宣告

如果只在標頭檔案的型別宣告中使用協議,可以指定前向引用。例如:

@protocol 類名;

協議適用性檢查

使用編譯器命令符@protocol()後,就可以獲得表示指定協議資料的指標。在執行時可以動態地檢查物件是否適用於某個協議。@protoco()引數中包含型別(Protocol *),可以帶入變數。

+ (BOOL)conformsToProtocol:(Protocol *)aProtocol//aProtocol引數指定的協議和類適用時,返回YES。

- (

BOOL)conformsToProtocol:(Protocol *)aProtocol//aProtocol引數指定的協議和類適用時,返回YES。

檢查物件obj是否適用於協議NSLocking,可以用如下方式:

if([obj conformsToProtocol:@protocol(NSLocking)])...

必選功能和可選功能

在協議宣告中,編譯器命令符@optional和@required可用來設定其後出現的方法是可選的還是必選的。而@optional和@required命令符在宣告中以什麼樣的順序出現以及出現多少次都可以。如果宣告中沒用特殊指定,那麼就預設為@required,表示方法是必選的。

由於採用協議的類可以不實現可選方法,因此就需要動態的檢查方法是否可用。

非正式協議

什麼是非正式協議

將一組方法宣告為NSObject的範疇,就稱為非正式協議(informal protocol),或稱為簡化協議。

非正式協議只是作為範疇進行宣告,並沒有實現。範疇中宣告的方法即使沒有實現,也可以編譯或執行,但是傳送訊息時會出現執行時錯誤。

如果要檢查非正式協議中的方法是否已經實現,只能對每個方法呼叫respondsToSelector:

總結非正式協議的相關概念:

非正式協議被宣告為NSObject類的範疇

非正式協議中宣告的方法不一定要實現

編譯時,不能檢查類對非正式協議的適用性

執行時,不能檢查類對非正式協議的適用性。只能確認是否實現了每個方法。

相關文章