星期六, 10月 06, 2012

Objective-C 的協定 (Protocol)

前幾天在看 iOS 開發的翻譯書稿件, 注意到一件奇怪的事。在使用 UIAlertView 的時候, 有些書都沒有強調擔任 UIAlertView 的委派物件必須遵守 (conform) UIAlertViewDelegate 協定。因此, 如果是由 controller 擔負委派物件時, controller 的類別大概會是這樣:
@interface ViewController : UIViewController
    ....
- (void)alertView:(UIAlertView *)alertView
    clickedButtonAtIndex:(NSInteger)buttonIndex {
    .....
}
@end
事實證明, 這樣的確是可以運作, 連個編譯的錯誤訊息都沒有。當使用者點選了 UIAlertView 上的按鈕時, 的確會引發 clickedButtonAtIndex。

但若是使用 UIActionSheet 時, 書中卻又強調委派物件必須遵守 UIActionSheetDelegate 協定。因此, 如果一樣由 controller 擔任委派物件時, controller 類別大概會長這樣:
@interface ViewController :
        UIViewController<UIActionSheetDelegate>
.....
- (void)actionSheet:(UIActionSheet *)actionSheet
        clickedButtonAtIndex:(NSInteger)buttonIndex {
.....
}
@end
實在覺得很奇怪, 兩個元件的 pattern 其實是一樣的, 但為甚麼一個需要明確標示遵守協定, 另一個卻不用?經過 google 找到這一篇 Why do I not need to declare UIAlertViewDelegate in the header?, 看起來原因有兩個:
  1. 在建立 UIAlertView 的  initXXX 方法中, delegate: 的型態是 delegate:(id), 並沒有要求委派物件必須遵守任何協定。但是在建立 UIActionSheet 的 initXXX 方法中, delegate: 的型態卻是 delegate:(id < UIActionSheetDelegate >), 表示委派物件應該要遵守 UIActionSheetDelegate 協定。
  2. 在 Objective-C 中, 協定的角色似乎沒有其他種類物件導向語言中的界面 (Interface) 那樣嚴謹。事實上, 如果擔任 UIActionSheet 的委派物件並沒有遵守 UIActionSheetDelegate 協定,  只要實作有正確的 clickedButtonAtIndex: 方法, 程式一樣可以動, 只是編譯時會有警告訊息而已。而在 framework 的實作中, 看來也都是以透過 respondsToSelector:@selector(delegatedMethod:) 檢查委派物件是否具有指定的方法, 才會實際叫用該方法, 而不是依靠是否遵循特定的協定來判斷。
換句話說, 協定就只是給大家參考的規格書, 而不是像是其他語言中的界面那樣嚴格。 

沒有留言: