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. SQL判断临时表是否存在

    IF EXISTS(select * from tempdb..sysobjects where id=object_id('tempdb..#tb')) BEGIN DROP TABLE #tb E ...

  2. python_way,day8 面向对象【多态、成员--字段 方法 属性、成员修饰符、特殊成员、异常处理、设计模式之单例模式、模块:isinstance、issubclass】

    python_way day8 一.面向对象三大特性: 多态 二.面向对象中的成员 字段.方法属性 三.成员修饰符 四.特殊成员 __init__.__doc__.__call__.__setitem ...

  3. CI实践_Android持续集成

    之前已经实现了Android的持续集成,并在项目中应用了一段时间.恰逢现在有几分钟时间,把之前的一些零散的点滴记录和整理一下,供有需要的朋友参考,或后续复用. 需要的准备知识:gitlab.Jenki ...

  4. 对List中对象的去重

    今天项目中遇到了一个对List中对象去重的问题. 首先对于我们自己系统中的对象我们只要重写该对象的 equal 和 hashcode 即可(利用对象中的能够唯一确定对象的属性). 但是我遇到的不是本系 ...

  5. flex中实现自动换行

    有时候由于label .button等控件中需要用到text属性显示出文本,文本太长就涉及到换行问题,解决方法如下 在actionScript 需要用“  ”实现换行,在需要换行的地方加上它就OK. ...

  6. 动态CSS--less

    忙了很久终于有时间来写点东西了,不知道大家有没有发现,我们在写CSS的时候总是在重复很多代码,一个相同的属性值往往要重复N次,以前我就经常想有没有什么办法能让我们不用一直重复的font-size啊co ...

  7. windos多线程编程

    随机数滚动发生器 #include <stdio.h> #include <Windows.h> #include <ctime> #include <pro ...

  8. Java字符串处理函数

    substring() 它有两种形式,第一种是:String substring(int startIndex)第二种是:String substring(int startIndex,int end ...

  9. ttttttttttt

    http://www.2cto.com/kf/201606/519504.html http://a67474506.iteye.com/blog/2079590 spring boot: http: ...

  10. java集合和scala集合互转

    使用 scala.collection.JavaConverters 与Java集合交互.它有一系列的隐式转换,添加了asJava和asScala的转换方法.使用它们这些方法确保转换是显式的,有助于阅 ...