Objective-C:模擬按鈕點擊事件理解代理模式


  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就相當於方法(消息),發送消息就是調用方法


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM