OC中的協議(Protocol)和和.NET中的接口(Interface)類似,簡單來講,就是一系列方法的列表,其中聲明的方法可以被任何類實現。不同的是,在.NET中,如果某個類實現了一個接口,就必須實現這個接口中聲明的所有方法;但在OC中,可以不實現協議中聲明的所有方法,需要用到某些功能,就去實現對應的方法即可。
這種模式一般稱為代理模式。在iOS和OS X開發中,Apple采用了大量的代理模式來實現MVC中View(UI控件)和Controller(控制器)的解耦。
監聽思想:如果想讓某個對象能夠監聽某個控件的動作,就可以讓這個對象實現特定的協議,好抽象~。說人話,比如,如果想要用戶點擊某個按鈕后,APP能夠做一些事情(例如返回到上一個界面),需要給按鈕設置監聽器監聽按鈕的點擊事件,然后“返回上一個界面”這個事情就交給監聽器去做。因為需要作出特定的響應,所以不是所有的對象都可以成為按鈕的監聽器的。那什么樣的對象才能充當按鈕的監聽器呢?條件是:實現某個協議里的方法(這個協議一般在需要被監聽的對象內部聲明)。
以監聽按鈕的點擊事件為例:
Button:我可以被點,誰來監聽我啊?
//Button.h頭文件
//和分類Catelogy一樣,協議也可以寫在其他文件中,但一般寫在.h文件中
#import <Foundation/Foundation.h> @class Button; // <>代表實現某個協議 @protocol ButtonDelegate <NSObject>//協議也可以實現其他協議,定義的該協議實現了最根本的協議NSObject - (void)onClick:(Button *)btn;//這個方法是留給需要實現該協議的對象去調用 @end @interface Button : NSObject @property (nonatomic, retain) id<ButtonDelegate> delegate;//按鈕應提供一個屬性來設置監聽器,delegate就是按鈕的監聽器,不然按鈕和其監聽器無法通信
//不需要線程安全,故nonatomic;它是個對象,最好用retain;不知道聲明類型的對象會成為自己的監聽器,故id
- (void)click;//這個方法是按鈕自己調用的,用來模擬點擊按鈕事件,調用該方法相當於點擊了按鈕
@end
//Button.m實現文件
#import "Button.h" @implementation Button - (void)dealloc { [_delegate release]; [super dealloc]; } - (void)click { // 如果_delegate實現了onClick:這個方法 if ( [_delegate respondsToSelector:@selector(onClick:)] ) { // 按鈕被點擊了,就應該通知監聽器.並且告訴監聽器哪個按鈕被點擊了 [_delegate onClick:self]; } else { NSLog(@"監聽器並沒有實現onClick:方法"); } } @end
ButtonListerner:我想監聽你呢!
//ButtonListerner.h頭文件
#import <Foundation/Foundation.h> // 對協議進行提前聲明,跟@class的用途是一致的 @protocol ButtonDelegate; @interface ButtonListener : NSObject <ButtonDelegate>//要做Button的監聽器,就要實現我Button定的協議 @end
//ButtonListerner.m實現文件
#import "ButtonListener.h" #import "Button.h" @implementation ButtonListener - (void)onClick {//實現Button協議里為我准備的onClick方法 NSLog(@"ButtonListener已經監聽到按鈕被點擊了"); } @end
main:我來檢查下,看ButtonListerner可不可以監聽Button了。
//main函數中實現對按鈕的監聽
#import <Foundation/Foundation.h> #import "Button.h" #import "ButtonListener.h" int main(int argc, const char * argv[]) { @autoreleasepool { // 初始化一個按鈕 Button *button = [[[Button alloc] init] autorelease]; // 初始化一個按鈕的監聽器 ButtonListener *listener = [[[ButtonListener alloc] init] autorelease];// 設置按鈕的監聽器 button.delegate = listener; NSLog(@"button:%@", button); // 點擊按鈕 [button click]; } return 0; }
main函數運行后打印的結果是:ButtonListener已經監聽到按鈕被點擊了。
這表明ButtonListerner里的onClick方法被調用了,奇怪,在main函數中我們並沒有調用這個方法呀。這就是我們想要的結果:當“[button click];”執行(前面說過,這表示Button被點擊了),然后某些事情就自動處理了(比如這里ButtonListerner里的onClick方法)。再啰嗦幾句吧。
理解這個監聽過程,主要注意這兩個方法:
- - (void)click方法:這個方法是按鈕自己實現的,相當於事件的發生;
- - (void)onClick:方法:這個方法是代理去實現的。
上面的例子顯示了當按鈕Button調用自己的click方法時,跟着按鈕的代理ButtonListener的onClick方法也被調用了,這樣就達到了監聽按鈕點擊事件的目的。也就是說,當我們想讓一個按鈕被點擊后,其他地方能夠進行一些各種各樣的操作時,我們不需要在按鈕內部去實現,在它的代理對象內部去實現就可以啦,這不就達到了解耦的目的嘛。
協議也可以寫在一個新建的協議文件里,並且實現該協議的對象可以對該協議里的方法選擇性地去實現。哪些方法是必須實現的,哪些方法又是可以選擇性實現的呢?這取決於該方法是如何被聲明的。
定義兩個協議:
//StudyDelegate.h 協議文件
#import <Foundation/Foundation.h> @protocol StudyDelegate <NSObject> // 默認就是@required - (void)test3; // @required表示必須實現的方法 // 雖然字面上說是必須實現,但是編譯器並不強求某個類進行實現 @required - (void)test; - (void)test1; // @optional表示可選(可實現\也可不實現) @optional - (void)test2; @end
//LearnDelegate.h協議文件
#import <Foundation/Foundation.h> @protocol LearnDelegate <NSObject> @end
定義一個學生類,該類的對象用來實現上面的協議:
//Student.h
#import <Foundation/Foundation.h> @protocol StudyDelegate, LearnDelegate; @interface Student : NSObject <Study, Learn> @end
//Student.m
#import "Student.h" #import "StudyDelegate.h" #import "LearnDelegate.h" @implementation Student @end
判斷Student類型的對象是否實現了協議:
#import <Foundation/Foundation.h> #import "Student.h" @protocol StudyDelegate; int main(int argc, const char * argv[]) { @autoreleasepool { Student *stu = [[[Student alloc] init] autorelease]; // 注意:OC是弱語法的,對類型要求不嚴格 // NSString *stu = [[[Student alloc] init] autorelease]; // [stu stringByAbbreviatingWithTildeInPath]; // conformsToProtocol:判斷是否遵守了某個協議 if ([stu conformsToProtocol:@protocol(StudyDelegate)]) { NSLog(@"Student遵守了StudyDelegate這個協議"); } // respondsToSelector:判斷是否實現了某個方法 if ( ![stu respondsToSelector:@selector(test)] ) { NSLog(@"Student沒有實現test這個方法"); } } return 0; }
運行結果是:Student遵守了StudyDelegate這個協議;Student沒有實現test這個方法;驗證了剛才的說法:可以遵守協議,但不實現該協議里聲明的方法。遵守協議即實現協議。
注意,selector就相當於方法(消息),發送消息就是調用方法。