Objective-C學(xué)習(xí)之路 委托模式
Objective-C學(xué)習(xí)之路 委托模式是本文要介紹內(nèi)容,委托模式很重要,比如官方交互API,委托模式使用的很常見,比如UIView的setAnimationDelegate,設(shè)置動(dòng)畫的委托。不理解委托模式,就不能很快的理解很多API的使用,因?yàn)樗鼈兪褂靡粯拥哪J?,了解這個(gè)模式,就會(huì)心領(lǐng)神會(huì),立即上手。
下面用通俗的話說說委托模式是干什么用的。實(shí)際上Objective-C中的委托模式,類似于Java中的回調(diào)(CallBack)機(jī)制,或者說監(jiān)聽器機(jī)制。再或者說,類似JavaScript語言里面的onclick事件和函數(shù)的作用。比如要實(shí)現(xiàn)點(diǎn)擊一個(gè)按鈕之后做什么事情,這里肯定有個(gè)視圖類,有個(gè)控制類,無論你是使用什么語言和開發(fā)工具。視圖類能知道用戶什么時(shí)候點(diǎn)擊了按鈕,但是不知道點(diǎn)擊了以后做什么,控制類知道點(diǎn)擊按鈕后做什么,而不知道何時(shí)用戶會(huì)點(diǎn)擊。那么,可以將控制類委托給視圖類,當(dāng)點(diǎn)擊的時(shí)候視圖類調(diào)用控制類。
如果使用過Java的Swing等做本地圖形界面開發(fā),應(yīng)該知道在視圖類中包含了大量的(匿名)內(nèi)部類,或者要注冊(cè)監(jiān)聽器,這些機(jī)制起到和Objective-C委托類似的功效??梢赃@樣理解:監(jiān)聽器、(匿名)內(nèi)部類是實(shí)現(xiàn)怎么做的部分,但是不知道何時(shí)會(huì)發(fā)生事情,視圖類在事件發(fā)送時(shí)調(diào)用監(jiān)聽器、(匿名)內(nèi)部類,視圖類是知道何時(shí)發(fā)生事情的。
寫個(gè)簡(jiǎn)單的示例,是在main方法里寫的,模擬一下委托在視圖和控制中的作用。這里面,我有一個(gè)屏幕(Screen)類,就把它當(dāng)視圖吧。需求是當(dāng)點(diǎn)擊屏幕的時(shí)候爆炸。那么我有個(gè)動(dòng)作(Action)類,它會(huì)實(shí)現(xiàn)爆炸動(dòng)作。
用協(xié)議實(shí)現(xiàn)委托模式
下面的代碼寫的很生硬,后面會(huì)逐漸演化為合理的實(shí)現(xiàn)。第一個(gè)示例只是想說明技術(shù)上如何實(shí)現(xiàn),沒有實(shí)際運(yùn)用上的意義。
這里因?yàn)槭悄M,可以把main方法看作是用戶再操作界面,通過點(diǎn)擊創(chuàng)建了個(gè)視圖(Screen),然后調(diào)用Screen的實(shí)例方法onTouch,這里模擬用戶用手點(diǎn)擊了屏幕:
- #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類,在這里用協(xié)議來實(shí)現(xiàn):
- #import <Cocoa/Cocoa.h>
- @protocol Action <NSObject>
- - (void) doAction;
- @end
是一個(gè)協(xié)議,該協(xié)議繼承了NSObject協(xié)議。這里要注意,NSObject在這里不是類,確實(shí)有同名類。這個(gè)協(xié)議定義了一個(gè)doAction方法,這個(gè)方法可實(shí)現(xiàn)比如“屏幕爆炸”的需求。
下面說說屏幕(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被用戶點(diǎn)擊后調(diào)用的方法。Screen類實(shí)現(xiàn)了Action協(xié)議。然后它還有個(gè)Action類型的成員delegate。為了能設(shè)置delegate實(shí)例變量,還為它設(shè)置了property。
下面看看實(shí)現(xiàn)文件:
- #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
這里重點(diǎn)看onTouch方法內(nèi)部代碼,要判斷delegate是否是Action協(xié)議,而且是否有doAction方法,這個(gè)判斷夠嚴(yán)謹(jǐn)了。如果正確,就調(diào)用Action協(xié)議的doAction方法。
實(shí)際上未必要讓Screen實(shí)現(xiàn)Action協(xié)議,雖然開發(fā)中經(jīng)常是類似這樣的做法。任意的實(shí)現(xiàn)Action協(xié)議的類實(shí)例都可以設(shè)置給screen的delegate屬性。
上面的示例和開發(fā)中碰到的情況不很像,實(shí)際情況往往類似下面示例的樣子。首先看看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;
- }
這里發(fā)現(xiàn)增加了個(gè)MyScreen 類。它繼承自Screen類。這里的代碼不再設(shè)置delegate屬性,因?yàn)橐呀?jīng)在MyScreen類的init方法中設(shè)置了。后面會(huì)看到。
Action協(xié)議沒有變化,只是增加了optional:
- #import <Cocoa/Cocoa.h>
- @protocol Action <NSObject>
- @optional
- - (void) doAction;
- @end
Screen類,可以看作抽象類,它主要供繼承使用,來復(fù)用委托模式的代碼。頭文件:
- #import <Foundation/Foundation.h>
- #import "Action.h"
- @interface Screen : NSObject <Action> {
- id <Action> delegate;
- }
- @property(nonatomic,retain) id <Action> delegate;
- - (void) onTouch;
- @end
實(shí)現(xiàn)文件:
- #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
這里不再實(shí)現(xiàn)doAction方法。
下面看MyScreen類的頭文件:
- #import <Cocoa/Cocoa.h>
- #import "Screen.h"
- @interface MyScreen : Screen {
- }
- @end
MyScreen類的實(shí)現(xiàn)文件:
- #import "MyScreen.h"
- @implementation MyScreen
- - (id) init{
- if (self=[super init]) {
- delegate=self;
- }
- return self;
- }
- - (void) doAction{
- NSLog(@"Bang!!!!!!!!!");
- }
- @end
用類別實(shí)現(xiàn)委托模式
可以使用類別(Category)實(shí)現(xiàn)委托模式。還是上面的例子。下面使用Category實(shí)現(xiàn)了個(gè)示例。
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
在這個(gè)示例中,實(shí)際上property沒有起什么作用。
實(shí)現(xiàn)文件:
- #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
寫到這里,如果運(yùn)行代碼,只會(huì)打印類似下面的日志:
- 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
實(shí)現(xiàn)文件:
- #import "ScreenAction.h"
- @implementation Screen (ScreenAction)
- - (void) doAction{
- NSLog(@"BANG!!!!!!");
- }
- @end
實(shí)現(xiàn)了這部分代碼再執(zhí)行:
- 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.
小結(jié):Objective-C學(xué)習(xí)之路 委托模式的內(nèi)容介紹完了,希望本文對(duì)你有所幫助。