#import逐行解釋這個header裡頭的內容:@class MyButton; @protocol MyButtonDelegate - (void)myButtonDidBecomeClicked:(MyButton *)button; - (void)myButtonDidBecomeDoubleClicked:(MyButton *)button; - (void)myButtonDidBecomeTripleClicked:(MyButton *)button; - (void)myButtonDidBecomeQuadrupleClicker:(MyButton *)button; @end @interface MyButton : NSButton { id delegate; } @protocol (weak, nonatomic) id delegate; @end
1. #import<Cocoa/Cocoa.h>:因為NSButton是在AppKit裡頭,
所以我們必須呼叫對應的header
2. @class MyButton; :因為在我們接下來的protocol宣告中,會用到MyButton
這個class,但是MyButton其實是宣告在MyButtonDelegate的下面,所以我們需要
先預先宣告MyButton這個class的存在。
3.從@protocol MyButtonDelegate開始,就是在宣告MyButtonDelegate
這個protocol裡頭的四個method。
4. 接下來宣告MyButton這個class, 裡頭有一個叫做delegate的變數。
實作Delegate Methods
假如我們有一個叫做MyController的controller物件,要成為MyButton的
delegate, 我們會這麼做。首先是.h部分:
@interface MyController : NSObject我們要先宣告MyController有實作MyButtonDelegate這個protocol,如此一來,{ IBOutlet MyButton *myButton; } @end
假如MyController漏了實作那些定義在MyButtonDelegate裡頭的method,
在編譯的時候就會跳出警告,要求我們修正,如果我們還是不實作的話,執行時,
就會發生找不到selector對應的實作的錯誤而crash。
如果MyController又是其他物件的delegate的話,我們可以在這段用大於與小於間包起來的宣告,繼續加入其他的protocol的名稱,例如<MyButtonDelegate, AnotherProtocol>。
至於一個物件是谷有實作某個protocol,我們可以用conformsToProtocol:檢查。
在MyController的實作中,我們就只要將myButton的delegate設成自已,然後實作該實作的method。我個人不太喜歡在protocol的宣告中出現大量註解,因為在實作protocol的時候,最方便的方式就是直接把protocol宣告的method複製貼上,接著逐一把肉放集protocol定義的骨幹裡;如果當中出現註解,複製貼上之後,還要把這些註解刪掉,其實還挺煩人。
@implementation MyController
- (void)awakeFromNib {
[myButton setDelegate:self];
}
#pragma mark - MyButtonDelegate
- (void)myButtonDidBecomeClicked:(MyButton *)button
{
}
- (void)myButtonDidBecomeDoubleClicked:(MyButton *)button
{
}
- (void)myButtonDidBecomeTripleClicked:(MyButton *)button
{
}
- (void)myButtonDidBecomeQuadrupleClicker:(MyButton *)button
{
}
@end
我們在這邊透過setDelegate:指定delegate,如果我們把delegate宣告成是一個IBOutlet的話, 也可以直接在Interface Builder中連結。
Delegate Methods是怎麼被呼叫的?
MyButton是怎樣呼叫delegate的呢?其實很簡單。
@implementation MyButton
- (void)mouseDown:(NSEvent *)theEvent {
switch ([theEvent clickCount]) {
case 1:
[delegate MyButtonDidBecomeClicked:self];
break;
case 2:
[delegate myButtonDidBecomeDoubleClicked:self];
break;
case 3:
[delegate myButtonDidBecomeTripleClicked:self];
break;
case 4:
[delegate myButtonDidBecomeQuadrupleClicker:self];
break;
default:
break;
}
}
@synthesize delegate;
@end
注意事項
在上面的範例中,我們看到了設計delegate與protocol應該注意的地方:
Delegate物件不應該指定Class
我們將delegate物件宣告成id<MyButtonDelegate> delegate,意思就是
不需要管這個物件是屬於那個class,只要是個Objective-C物件即可,但是這個
物件必須實作MyButtonDelegate protocol。
我們其實可以將delegate物件是那個class寫死,例如把MyButton的delegate的class
指成MyController,但這樣做非常不好,如此一來,就只有MyController可以使用MyButton,其它controller都無法使用,就大大減少了重複使用MyButton的彈性。
Delegate這種設計方式,也方便我們在同時開發Mac OS X與iOS跟平台專案時共用程式碼,我們在撰寫某個model物件的時候,只使用Foundation或是其他兩個平台都有的framework,至於與平台相依的部分,就放進delegate中,然後在Mac OS X與iOS上各自實作delegate物件。
總之,在實作delegate的時候delegate屬於那個class並不重要,重要的是delegate物件有沒有實作我們想要呼叫的method。
Delegate屬性應該要用Weak,而非Strong
在使用property語法的時候,如果這個property是Objective-C物件,我們照說應該要設定成strong或retain,但是遇到的是delegate,我們應該設成weak或assign。
原因是:需要設計delegate物件這個物件,往往是其delegate物件的成員變數。
在我們的例子中,MyButton的instance是myButton,是MyController的成員變數,
自已可能已經被MyController retain了一份。如果MyButton又retain了一次MyController,就出循環retain的問題,我已經被別人retain,我又把別人retain一次。
如此會造成我們無法釋放MyController:在該釋放MyController的時候,MyController還是被自已的成員變數retain,MyController得要走到dealloc才會釋放myButton,但是自已卻因為被myButton給retain起來,而始終走不到dealloc。
Delegate Method的命名方式
Delegate method的命名有個鮮明的特色,就是這個method至少會傳入一個參數,就是把到底是誰呼叫了這個delegate method傳遞進來。同時,這個method也往往以傳入的class名稱開頭,讓我們可以辯識這是屬於那個class的delegate method。以UITableViewDelegate為例,假如我們在iOS的表格中選擇了某一列,就會呼叫
- (void)tableView:(UITablewView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
Method的名稱就以「tableView」開頭,讓我們知道這是屬於Table View的delegate method,然後第一個參數這個Table View的instance傳入,接下來才傳入到底是那一列被選起 資訊。
至少把是誰呼叫了這個deegate method傳入的理由很簡單。
以我們的MyController為例,這 個controller可能有好幾個MyButton,而這些MyButton
全都把delegate指到同一個controller上,那麼controller就需要知道,到底是被那個button呼叫。判斷方式只要簡單比對指標就好了:
-(void)myButtonDidBecomeQuadrupleClicked:(MyButton *)button
{
if(button == myButton1) {
} else if (button == myButton2) {
}
}
沒有留言:
張貼留言