en

hi, it seems you are using microsoft internet explorer. it doesn't match web standard and causes problems browsing this site. please please please use mozilla firefox or google chrome instead. thank you!

zh

哦哦!您正在使用Internet Explorer 瀏覽器,它與我們的網頁標準並不相容,可能會導致畫面顯示不正常。
請改用 Mozilla Firefox 或者 Google Chrome 才能正常瀏覽本網站,謝謝!

1.06.2012

Protocol 協定的使用方式

Protocol 協定,在是一種很常被使用的技巧,它可以將該類別的方法分享給其他的類別使用,替一個類別定義協定,並將要分享的方法寫在此協定的區塊中,之後在其他的類別中實作出此方法,這就是製作協定的基本流程,下面就讓我們來看看協定的使用方法。


製作 Protocol
要為一個類別定義一個協定,必須使用 @protocol 區段來定義它,它至少必須包含協定的名稱與協定的內容方法,協定名稱的命名方式,通常是以該類別的名稱加上 Delegate 來表示,避免混淆。以下是替 Furnace 類別定義一個協定的程式碼範例(類別並沒有限定能定義協定的數目)。
@protocol FurnaceDelegate

    - (void)whenCalledDelegeteFunction;

@end


Protocol 的生效位置
在定義協定之後,還必須定義它何時生效,就像合約都有生效日期一般,協定的生效位置則是定義在程式碼裡,並且生效的位置不應該在類別的建構式中,如果協定內同時定義許多方法,則可以在不同的位置,分別使它們生效。同樣我們在 Furnace 類別中宣告一個採納此協定的變數,採納協定的方法是使用角括號 <協定名稱> 來表示,變數的名稱則是使用大家通用的變數名稱 delegate,避免混淆。
@property (weak) id<FurnaceDelegate> delegate;
採納協定的 delegate 變數它必定含有協定中所定義的方法 ,現在我們要透過此變數來呼叫協定內所宣告的方法,來決定協生效的位置。替 Furnace 類別建立一個新的方法函式,並讓協定在此生效。
-(void) delegateFunction {
    [delegate whenCalledDelegeteFunction];
}


使用 Protocol
到目前為止,我們並沒有實作協定內的方法,其原因是其實我們並不知道要在哪裡實作它,協定內的方法並不需要也不會在定義此協定的類別內被實作,如同和約履約的對象,通常不會也不應該是自己,所以這表示我們將在其他的類別中實作此協定內的方法。

建立一個新的類別(範例中是使用 Template 所給的 ViewController 類別),並在 @interface 區段使用角括號 <協定名稱> 來採納此協定,你也可以使用 <協定名稱, 協定名稱> 同時採納多個協定,由於協定是定義在另一個類別用,所以在採納前務必先 #import。
#import "Furnace.h"

    @interface MLViewController : UIViewController <FurnaceDelegate>

@end

好了,現在我們建立的新類別已經採納 FurnaceDelegate 協定,這也表示我們必須在這個類別中實作協定內的方法(如果不實作協定內的方法, Xcode 會發出警告訊息)。
- (void)whenCalledDelegeteFunction {
    NSLog(@"Hi~ Delegate!!");
}

現在一切就緒,只要等協定生效,就會觸發協定內的方法傾印出 Hi~ Delegate!! 的字串,但是要如何使協定生效呢?還記得協定的生效位置嗎,之前我們將它定義在 Furnace 類別的 delegateFunction 方法中,為了能使協定生效,所以我們必須宣告一個 Furnace 形態的物件來呼叫此方法,完成協定的生效動作。
Furnace *iOS = [[Furnace alloc] init];
iOS.delegate = self;
[iOS delegateFunction];


Protocol 的其它問題
  • 使用時為什麼要加上 iOS.delegate = self
物件名稱.delegate = self,是在採用任何協定時 一定會看到的一行程式碼,由於定義協定的類別並不需要實作協定內的方法,因為實作的部份是由採納協定的類別來實作,但是它又必須要知道是由哪一個類別來實作,因此我們必須要把採納協定類別的 instance 交給定義協定的類別,讓它來使用。 
另一方面並不是任何類別都可以將 instance 傳給定義協定的類別來使用,其原因是,我們在定義此協定的類別裡有宣告 delegate 變數時,有限定它必須要採納此協定(id<FurnaceDelegate> delegate)如果沒有採用該協定就將 instance 傳給定義該協定的類別,Xcode 同樣會發出警告訊息。

  • 為什麼協定的生效位置不能寫在建構式中
協定的生效位置寫在建構式中,並不會造成程式編譯上的任何問題,因為這是屬於邏輯上的錯誤,協定要正常生效它必須要知道實作它方法的類別的 instance,如果將生效的位置寫在建構式中,在建立定義此協定的形態的變物件時,它的確會去觸發此協定內的方法,但是由於並沒有給它實作此協定方法類別的 instance,因此不會有任何效果產生,反之,如果一定要將生效的位置寫在建構式中,那麼在初始化時就必須要設定好 delegate 才行,也就是使用初始化的方法函式裡還必須要帶入一個參數物件好指定給 delegate。

  • 在定義協定時同時也可以採用其他的協定
如果在定義協定時同時又採用其他的協定,這會導致之後採納此協定的類別,它必須同時實作出兩個協定內的方法,同樣地,你也可以利用此方式來擴充那些已經存在的協定。
@protocol FurnaceDelegate <其它可能的協定名稱>


  • 使用 @optional 提供選擇性的實作
@optional,如同它字面上的意義,在 @optional 之後的方法都可以是選擇性的實作,在定義協定時使用此方法,可以讓之後採納此協定的類別不一定要完全實作出協定內的所有方法。

@protocol FurnaceDelegate
    - (void)whenCalledDelegeteFunction;

@optional
    -(void)optionalDelegeteFunction;

@end






沒有留言:

張貼留言