OC中的协议(Protocol)和和.NET中的接口(Interface)类似,简单来讲,就是一系列方法的列表,其中声明的方法可以被任何类实现。不同的是,在.NET中,如果某个类实现了一个接口,就必须实现这个接口中声明的所有方法;但在OC中,可以不实现协议中声明的所有方法,需要用到某些功能,就去实现对应的方法即可。
这种模式一般称为 代理模式 。在iOS和OS X开发中,Apple采用了大量的代理模式来实现MVC中View(UI控件)和Controller(控制器)的 解耦 。
监听思想:如果想让某个对象能够监听某个控件的动作,就可以让这个对象实现特定的协议,好抽象~。说人话,比如,如果想要用户点击某个按钮后,APP能够做一些事情(例如返回到上一个界面),需要给按钮设置监听器监听按钮的点击事件,然后“返回上一个界面”这个事情就交给监听器去做。因为需要作出特定的响应,所以不是所有的对象都可以成为按钮的监听器的。那什么样的对象才能充当按钮的监听器呢?条件是:实现某个协议里的方法(这个协议一般在需要被监听的对象内部声明)。
以监听按钮的点击事件为例:
//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.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函数中实现对按钮的监听 #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方法)。再啰嗦几句吧。
理解这个监听过程,主要注意这两个方法:
上面的例子显示了当按钮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就相当于方法(消息),发送消息就是调用方法 。