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 ;
}

  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 ;
}

运行结果是:Student遵守了StudyDelegate这个协议;Student没有实现test这个方法;验证了刚才的说法:可以遵守协议,但不实现该协议里声明的方法。遵守协议即实现协议。

注意,selector就相当于方法(消息),发送消息就是调用方法。

Objective-C:模拟按钮点击事件理解代理模式的更多相关文章

  1. C++用PostMessage模拟按钮点击

    有时我们可能会在某个程序中用到模拟按钮点击事件. 本文中的例子在MFC程序中调试通过,duilib的没试过,还需探索 不多说,上代码: #include "stdafx.h" #i ...

  2. js触发按钮点击事件

    js触发按钮点击事件 博客分类: javascript   模拟JS触发按钮点击功能 <html> <head> <title>usually function&l ...

  3. css3模拟jq点击事件

    还是这个梗,收好冷.今天是一个css3模拟jq点击事件,因为我发现,css3中没有类似于,js的点击事件,那么,可不可以仿照 jq的效果,类似的做一个呢?主要用到,input里面的radio 单选按钮 ...

  4. 按钮点击事件,打开新的Activity

    按钮点击事件,打开新Activity, 打开网页 findViewById(R.id.btnStartBAty).setOnClickListener(new View.OnClickListener ...

  5. Javascript之三种按钮点击事件

    学习Javascript必须要先掌握基本的事件方法和语法,这些都是我们学过的也是最基本的.以前忘了总结,所以现在回顾,综合地总结一下,温故而知新. Javascript有三种按钮点击事件,分别为ale ...

  6. 【转】Android开发20——单个监听器监听多个按钮点击事件

    原文网址:http://woshixy.blog.51cto.com/5637578/1093936 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律 ...

  7. 【Android】按钮点击事件的常用写法

    学习总结: 最近学习了Android点击事件的常用写法.点击事件会触发监听对象身上的回调,常用写法有以下四种: 方法一:使用匿名内部类. public class MainActivity exten ...

  8. ButtonAddListener监听按钮点击事件

    ButtonAddListener监听按钮点击事件 using UnityEngine; using System.Collections; using UnityEngine.UI; using U ...

  9. js和jquery触发按钮点击事件

    js触发按钮点击事件 function load(){ //下面两种方法效果是一样的 document.getElementById("target").onclick(); do ...

随机推荐

  1. Maven初学

    先上几张霸气的图:

  2. python_way day13 sqlalchemy

    sqlalchemy 一对多 多对多 1.一对多 一.#创建表结构 class Host(Base): #所有的子类都继承这个基类 #创建表结构 __tablename__ = 'hosts' id ...

  3. SQL Server 2005 中的同义词

    From : http://blog.csdn.net/itblog/article/details/752881 =============创建同义词 可以为下列对象类型创建同义词: 程序集 (CL ...

  4. java位运算符常见用法

    1. 判断int型变量a是奇数还是偶数 a&1 = 0 偶数 a&1 = 1 奇数 2. 求平均值,比如有两个int类型变量x.y,首先要求x+y的和,再除以2,但是有可能x+y的结果 ...

  5. nginx使用ssl模块配置HTTPS支持

    默认情况下ssl模块并未被安装,如果要使用该模块则需要在编译时指定–with-http_ssl_module参数,安装模块依赖于OpenSSL库和一些引用文件,通常这些文件并不在同一个软件包中.通常这 ...

  6. maven使用实例记录

    Maven入门指南① :Maven 快速入门及简单使用Maven入门指南② :Maven 常用命令,手动创建第一个 Maven 项目Maven入门指南③:坐标和依赖Maven入门指南④:仓库 Mave ...

  7. Win7_提高性能

    1. 设置成经典主题:桌面->右键->个性化->经典主题 2. 计算机->右键->属性->高级系统设置 ==> 系统属性->高级->性能-> ...

  8. mysql 检查字符串是否包含子串

    1.使用substring_index(src,target,index) 从src的开头查找第index个target.返回的substring为从src的开头到第num个target这段字符串.比 ...

  9. (二)程序中的内存&&栈

    一.程序运行为什么需要内存?基本概念? 内存是程序运行的立足之地,程序需要用内存来存储一些变量. 内存管理最终是由操作系统完成的,内存在本质上是一个硬件器件,由硬件系统提供:内存由操作系统统一管理,为 ...

  10. C++中的虚继承 & 重载隐藏覆盖的讨论

    虚继承这个东西用的真不多.估计也就是面试的时候会用到吧.. 可以看这篇文章:<关于C++中的虚拟继承的一些总结> 虚拟基类是为解决多重继承而出现的. 如:类D继承自类B1.B2,而类B1. ...