標籤:
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就相當於方法(訊息),發送訊息就是調用方法。
Objective-C:類比按鈕點擊事件理解代理模式