Objective-C學習之路 委托模式
Objective-C學習之路 委托模式是本文要介紹內容,委托模式很重要,比如官方交互API,委托模式使用的很常見,比如UIView的setAnimationDelegate,設置動畫的委托。不理解委托模式,就不能很快的理解很多API的使用,因為它們使用一樣的模式,了解這個模式,就會心領神會,立即上手。
下面用通俗的話說說委托模式是干什么用的。實際上Objective-C中的委托模式,類似于Java中的回調(CallBack)機制,或者說監聽器機制。再或者說,類似JavaScript語言里面的onclick事件和函數的作用。比如要實現點擊一個按鈕之后做什么事情,這里肯定有個視圖類,有個控制類,無論你是使用什么語言和開發工具。視圖類能知道用戶什么時候點擊了按鈕,但是不知道點擊了以后做什么,控制類知道點擊按鈕后做什么,而不知道何時用戶會點擊。那么,可以將控制類委托給視圖類,當點擊的時候視圖類調用控制類。
如果使用過Java的Swing等做本地圖形界面開發,應該知道在視圖類中包含了大量的(匿名)內部類,或者要注冊監聽器,這些機制起到和Objective-C委托類似的功效。可以這樣理解:監聽器、(匿名)內部類是實現怎么做的部分,但是不知道何時會發生事情,視圖類在事件發送時調用監聽器、(匿名)內部類,視圖類是知道何時發生事情的。
寫個簡單的示例,是在main方法里寫的,模擬一下委托在視圖和控制中的作用。這里面,我有一個屏幕(Screen)類,就把它當視圖吧。需求是當點擊屏幕的時候爆炸。那么我有個動作(Action)類,它會實現爆炸動作。
用協議實現委托模式
下面的代碼寫的很生硬,后面會逐漸演化為合理的實現。第一個示例只是想說明技術上如何實現,沒有實際運用上的意義。
這里因為是模擬,可以把main方法看作是用戶再操作界面,通過點擊創建了個視圖(Screen),然后調用Screen的實例方法onTouch,這里模擬用戶用手點擊了屏幕:
- #import <Foundation/Foundation.h>
- #import "Screen.h"
- int main (int argc, const char * argv[]) {
- NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
- Screen *screen=[[Screen alloc] init];
- screenscreen.delegate=screen;
- [screen onTouch];
- [screen release];
- [pool drain];
- return 0;
- }
這里先不用管:
- screenscreen.delegate=screen;
后面再說。
Action類,在這里用協議來實現:
- #import <Cocoa/Cocoa.h>
- @protocol Action <NSObject>
- - (void) doAction;
- @end
是一個協議,該協議繼承了NSObject協議。這里要注意,NSObject在這里不是類,確實有同名類。這個協議定義了一個doAction方法,這個方法可實現比如“屏幕爆炸”的需求。
下面說說屏幕(Screen)類,頭文件:
- #import <Foundation/Foundation.h>
- #import "Action.h"
- @interface Screen : NSObject <Action> {
- id <Action> delegate;
- }
- @property(nonatomic,retain) id <Action> delegate;
- - (void) onTouch;
- @end
這里的onTouch方法,就是模擬Screen被用戶點擊后調用的方法。Screen類實現了Action協議。然后它還有個Action類型的成員delegate。為了能設置delegate實例變量,還為它設置了property。
下面看看實現文件:
- #import "Screen.h"
- @implementation Screen
- @synthesize delegate;
- - (void) onTouch{
- NSLog(@"on touch …");
- if ([delegate conformsToProtocol:@protocol(Action)] &&
- [delegate respondsToSelector:@selector(doAction)]) {
- [delegate performSelector:@selector(doAction)];
- }
- NSLog(@"on touched.");
- }
- - (void) doAction{
- NSLog(@"Bang!!!!!!!!!");
- }
- @end
這里重點看onTouch方法內部代碼,要判斷delegate是否是Action協議,而且是否有doAction方法,這個判斷夠嚴謹了。如果正確,就調用Action協議的doAction方法。
實際上未必要讓Screen實現Action協議,雖然開發中經常是類似這樣的做法。任意的實現Action協議的類實例都可以設置給screen的delegate屬性。
上面的示例和開發中碰到的情況不很像,實際情況往往類似下面示例的樣子。首先看看main方法:
- #import <Foundation/Foundation.h>
- #import "MyScreen.h"
- int main (int argc, const char * argv[]) {
- NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
- Screen *screen=[[MyScreen alloc] init];
- [screen onTouch];
- [screen release];
- [pool drain];
- return 0;
- }
這里發現增加了個MyScreen 類。它繼承自Screen類。這里的代碼不再設置delegate屬性,因為已經在MyScreen類的init方法中設置了。后面會看到。
Action協議沒有變化,只是增加了optional:
- #import <Cocoa/Cocoa.h>
- @protocol Action <NSObject>
- @optional
- - (void) doAction;
- @end
Screen類,可以看作抽象類,它主要供繼承使用,來復用委托模式的代碼。頭文件:
- #import <Foundation/Foundation.h>
- #import "Action.h"
- @interface Screen : NSObject <Action> {
- id <Action> delegate;
- }
- @property(nonatomic,retain) id <Action> delegate;
- - (void) onTouch;
- @end
實現文件:
- #import "Screen.h"
- @implementation Screen
- @synthesize delegate;
- - (void) onTouch{
- NSLog(@"on touch …");
- if ([delegate conformsToProtocol:@protocol(Action)] &&
- [delegate respondsToSelector:@selector(doAction)]) {
- [delegate performSelector:@selector(doAction)];
- }
- NSLog(@"on touched.");
- }
- @end
這里不再實現doAction方法。
下面看MyScreen類的頭文件:
- #import <Cocoa/Cocoa.h>
- #import "Screen.h"
- @interface MyScreen : Screen {
- }
- @end
MyScreen類的實現文件:
- #import "MyScreen.h"
- @implementation MyScreen
- - (id) init{
- if (self=[super init]) {
- delegate=self;
- }
- return self;
- }
- - (void) doAction{
- NSLog(@"Bang!!!!!!!!!");
- }
- @end
用類別實現委托模式
可以使用類別(Category)實現委托模式。還是上面的例子。下面使用Category實現了個示例。
main方法:
- int main (int argc, const char * argv[]) {
- NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
- Screen *screen=[[Screen alloc] init];
- [screen onTouch];
- [screen release];
- [pool drain];
- return 0;
- }
Screen類的頭文件:
- #import <Foundation/Foundation.h>
- @interface Screen : NSObject {
- id delegate;
- }
- @property(nonatomic,retain) id delegate;
- - (void) onTouch;
- @end
在這個示例中,實際上property沒有起什么作用。
實現文件:
- #import "Screen.h"
- @implementation Screen
- @synthesize delegate;
- - (id) init{
- if (self=[super init]) {
- delegate=self;
- }
- return self;
- }
- - (void) onTouch{
- NSLog(@"on touch …");
- if ([delegate respondsToSelector:@selector(doAction)]) {
- [delegate performSelector:@selector(doAction)];
- }
- NSLog(@"on touched.");
- }
- @end
寫到這里,如果運行代碼,只會打印類似下面的日志:
- 2011-05-26 10:37:30.843 DelegateDemo[5853:a0f] on touch …
- 2011-05-26 10:37:30.846 DelegateDemo[5853:a0f] on touched.
下面寫Category代碼,名稱為ScreenAction,它的頭文件:
- #import <Cocoa/Cocoa.h>
- #import "Screen.h"
- @interface Screen (ScreenAction)
- - (void) doAction;
- @end
實現文件:
- #import "ScreenAction.h"
- @implementation Screen (ScreenAction)
- - (void) doAction{
- NSLog(@"BANG!!!!!!");
- }
- @end
實現了這部分代碼再執行:
- 2011-05-26 10:37:30.843 DelegateDemo[5853:a0f] on touch …
- 2011-05-26 10:37:30.846 DelegateDemo[5853:a0f] BANG!!!!!!
- 2011-05-26 10:37:30.846 DelegateDemo[5853:a0f] on touched.
小結:Objective-C學習之路 委托模式的內容介紹完了,希望本文對你有所幫助。