UITableViewController在loadView中建立了一個UITableView的instance,
指定成是自已的view,同時將這個view的delegate與data source設定成自已。
一個class可以根據需要,將delegate拆成好幾個,以UITableView來說,
跟表格中有什麼資料有關的,就放在data source中,其餘的method就放在delegate中。
我們在Mac OX X會用到的最龐大的UI元件,莫過於WebView,雖然在iOS上UIWebView被閹割到只剩下四個delegate method,但是Mac OS X上足足有五大類delegate method,網頁頁框的載入進度、個別圖片檔案的載入進度、下載檔案的UI呈現,該不該開視窗或新分頁、沒有安裝Java或是Flash要怎麼呈現、用JavaScript跳出alert該怎麼呈現...都是一堆delegate method。
假如先不管UITableView怎麼重複使用UITableViewCell的機制(這個機制還挺複雜),
我們要更新UITableView的資料時,先指定data source物件後,要呼叫一次reloadData。
reloadData可能是這樣寫的:
- (void)reloadData {
NSInteger sections = 1;
if([dataSource respondsToSelector:@selector(numberOfSectionsInTableView:)]) {
sections = [dataSource numberOfSectionsInTableView:self];
}
for (NSInteger section = 0; section < sections; section++) {
NSInteger rows = [dataSource tableView:self numberOfRowsInSection:section];
for (NSInteger row = 0; row < rows; row++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
UITableViewCell *cell = [dataSource tableView:Self cellForRowAtIndexPath:indexPath];
}
}
}
我們注意到幾件事情:首先,因為numberOfSectionsInTableView:被定義成optional的delegate method, delegate不見得要實作,所以我們會用respondsToSelector:
檢查是否有實作。我們可以在protocol的宣告中,指定某個delegate method是required或是optional,如果不特別指定的話,預設都是required。我們簡單看一下
UITableViewDataSource就知道如何定義required與optional的delegate method。
@protocol UITableViewDataSource <NSObject> @required - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section; - (UITableViewCell *)tableView:(UITableView *)tableVIew cellForRowAtIndexPath:(NSIndexPath *)indexPath; @optional - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView; @end
另外,就是定義在data source的method,是在reloadData中被呼叫,因此我們可以知道 UITableView的data source與delegate的最大差別:我們絕對不可以在data source定義的method中呼叫reloadData,不然就會進入無窮廻圈!
Formal Protocol與Informal Protocol
@protocol這個關鍵字是在Objective-C2.0之後出現的,在這之前要定義protocol,則是寫成NSObject的category,前者叫做formal protocol,後者則稱為informal protocol。
UIKit問世時就採用Objective-C2.0的語法,至於Max OS X,蘋果在2008年開始大幅改寫
Foundation與AppKit,現在絕大多數可以看到的protocol都是formal protocol,但如果
你在maintain一份稍微有點歷史的程式,或是在蘋果少數的API中,還是可以看到informal protocol。在Core Animation裡頭,就可以看到CALayerDelegate、CALayoutManager、CAAnimationDelegate,都還是informal protocol。其中CALayerDelegate、CALayoutManager兩者之間還夾著CAAction這個formal protocol。在兩個informal protocol中間夾著一個formal protocol,實在讓人很反感,為什麼不一起改掉呢?至於CAAnimationDelegate也很怪異:CAAnimation的delegate不是用assign,而是會retain起來。
無所不在的Delegate
由於在Objective-C語言中,delegate相當於event handler的用途,所以,當你在其他平台中看到event handler用得多頻繁,就等於delegate用得多頻繁。
舉例來說:
* 在使用NSURLConnection抓取網路上的資料的時候,無論收到了HTTP response code、是否連線失敗,是否連線結束...都是透過delegate回傳。
* 在使用Core Location的時候,如果CLLocationManager找到了我們的所在位置,或是發現我們正在移動,也都會透過delegate通知。
* 當我們要使用手機拍照、傳送簡訊或是電子郵件等等,當照片拍完,會用delegate回傳image物件,簡訊或是電子郵件傳送成功,也會用delegate告訴我們執行完畢。
甚至當我們在寫一個iOS程式的第一步,其實都是在實作一個delegate mehtod。
我們在Xcode裡頭開了一個新專案之後,下一步往往是實作
application:didFinishLaunchingWithOptions:這個method,但是要了解整個程式的進入點,我們要從main.m來看。裡頭通常只有簡短幾行:
int main(int argc, char *argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
一個iOS程式是從main這個function開始,接著透過呼叫UIApplicationMain建立UIApplication這個Singleton物件UIApplication用來代表一個應用程式的基本狀態,包括icon上面該顯示多少push notification的數量、支援水平還是郵直畫面、是否顯示狀態列等,當UIApplication物件被建立起來後,就要通知它的delegate,程式已經開啟了,進行下一步,這個delegate method就是application:didFinishLaunchingWithOptions:,我們在這邊建立基本的view controller與window,顯示出來。也就是說,當我們在開始寫第一行iOS程式的時候,我們就起碼需要了解什麼是Singleton和delegate,但是在了解之後,想要知道Mac OS X與iOS中眾多的元件如何使用,以及怎樣用比較好的方式設計自已的元件,就不是問題了。
其他平台上所謂的Delegate
在其他平台中,也用到了delegate這個詞,但是意義不太一樣。
Design Pattern中所講的Delegate
就我的理解,Design Pattern中所講的Delegate Pattern,比較像是做一個Wrapper,
有一個class在實作method時,其實是直接把這個method的實作傳遞到自已的成員變數物件的實作上。以Objective-C語言實作會像這樣:
首先產生一個內部的物件,叫做MyInnerClass:
@interface MyInnerClass : NSObject
- (void)doSomething;
@end
@implementation MyInnerClass
- (void)doSomething {
NSLog(@"Do something");
}
@end
然後MyClass會把該做的事情,都交給MyInnerClass:@interface MyClass : NSObject {
MyInnerClass *innerObject;
}
- (void)doSomething;
@end
@implementation MyClass
- (void)dealloc {
[innerObject release];
[super dealloc];
}
- (id)init {
self = [super init];
if (self) {
innerObject = [[MyInnerClass alloc] init];
}
return self;
}
- (void)doSomething {
[innerObject doSomething];
}
@end
在Cocoa Framework中,會比較像是NSButton與NSButtonCell的關係。你或許會問,為什麼Objective-C裡頭的delegate與Design Pattern裡頭講的Delegate Pattern意義不一樣呢?為什麼Objective-C不按照這套用法?但其實是,Objective-C使用delegate這個觀念,早於Design Pattern成書。C#中所謂的Delegate
C#語言中也有delegate這個關鍵字、不過用途卻是處理anonymous function,以我們上面的例子,我們打C#增加被點選的event handler,原本這麼寫:
private void InitializeComponent() {
this.button1 = new System.Windows.Forms.Button();
this.button1.Click += Button1_Click;
}
private void Button1_Click(object sender, System.EventArgs e) {
}
在C#2.0可以寫成這樣:
private void InitializeComponent() {
this.button1 = new System.Windows.Forms.Button();
this.button1.Click += delegate(object sender, System.EventArgs e) {
// Do something here.
}
}
關於在Objective-C語言中怎麼使用anonymous function,我們會在接下來的章節,講block的時候討論。
沒有留言:
張貼留言