ObjC: 委托模式
转自:http://marshal.easymorse.com/tech/objc-%e5%a7%94%e6%89%98%e6%a8%a1%e5%bc%8f
在ObjC中,经常提到委托模式(delegate),非常重要。比如官方交互API,委托模式使用的很常见,比如UIView的setAnimationDelegate,设置动画的委托。不理解委托模式,就不能很快的理解很多API的使用,因为它们使用一样的模式,了解这个模式,就会心领神会,立即上手。
下面用通俗的话说说委托模式是干什么用的。实际上ObjC中的委托模式,类似于Java中的回调(CallBack)机制,或者说监听器机制。再或者说,类似JavaScript语言里面的onclick事件和函数的作用。比如要实现点击一个按钮之后做什么事情,这里肯定有个视图类,有个控制类,无论你是使用什么语言和开发工具。视图类能知道用户什么时候点击了按钮,但是不知道点击了以后做什么,控制类知道点击按钮后做什么,而不知道何时用户会点击。那么,可以将控制类委托给视图类,当点击的时候视图类调用控制类。
如果使用过Java的Swing等做本地图形界面开发,应该知道在视图类中包含了大量的(匿名)内部类,或者要注册监听器,这些机制起到和ObjC委托类似的功效。可以这样理解:监听器、(匿名)内部类是实现怎么做的部分,但是不知道何时会发生事情,视图类在事件发送时调用监听器、(匿名)内部类,视图类是知道何时发生事情的。
写个简单的示例,是在main方法里写的,模拟一下委托在视图和控制中的作用。这里面,我有一个屏幕(Screen)类,就把它当视图吧。需求是当点击屏幕的时候爆炸。那么我有个动作(Action)类,它会实现爆炸动作。
用协议实现委托模式
下面的代码写的很生硬,后面会逐渐演化为合理的实现。第一个示例只是想说明技术上如何实现,没有实际运用上的意义。
这里因为是模拟,可以把main方法看作是用户再操作界面,通过点击创建了个视图(Screen),然后调用Screen的实例方法onTouch,这里模拟用户用手点击了屏幕:
#import <Foundation/Foundation.h>
#import "Screen.h"int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];Screen *screen=[[Screen alloc] init];
screen.delegate=screen;[screen onTouch];
[screen release];
[pool drain];
return 0;
}
这里先不用管:
screen.delegate=screen;
后面再说。
Action类,在这里用协议来实现:
#import <Cocoa/Cocoa.h>
@protocol Action <NSObject>
- (void) doAction;
@end
是一个协议,该协议继承了NSObject协议。这里要注意,NSObject在这里不是类,确实有同名类。这个协议定义了一个doAction方法,这个方法可实现比如“屏幕爆炸”的需求。
下面说说屏幕(Screen)类,头文件:
#import <Foundation/Foundation.h>
#import "Action.h"@interface Screen : NSObject <Action> {
id <Action> delegate;
}@property(nonatomic,retain) id <Action> delegate;
- (void) onTouch;
@end
这里的onTouch方法,就是模拟Screen被用户点击后调用的方法。Screen类实现了Action协议。然后它还有个Action类型的成员delegate。为了能设置delegate实例变量,还为它设置了property。
下面看看实现文件:
#import "Screen.h"
@implementation Screen
@synthesize delegate;
- (void) onTouch{
NSLog(@"on touch …");
if ([delegate conformsToProtocol:@protocol(Action)] &&
[delegate respondsToSelector:@selector(doAction)]) {
[delegate performSelector:@selector(doAction)];
}
NSLog(@"on touched.");
}- (void) doAction{
NSLog(@"Bang!!!!!!!!!");
}@end
这里重点看onTouch方法内部代码,要判断delegate是否是Action协议,而且是否有doAction方法,这个判断够严谨了。如果正确,就调用Action协议的doAction方法。
实际上未必要让Screen实现Action协议,虽然开发中经常是类似这样的做法。任意的实现Action协议的类实例都可以设置给screen的delegate属性。
上面的示例和开发中碰到的情况不很像,实际情况往往类似下面示例的样子。首先看看main方法:
#import <Foundation/Foundation.h>
#import "MyScreen.h"int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];Screen *screen=[[MyScreen alloc] init];
[screen onTouch];
[screen release];
[pool drain];
return 0;
}
这里发现增加了个MyScreen 类。它继承自Screen类。这里的代码不再设置delegate属性,因为已经在MyScreen类的init方法中设置了。后面会看到。
Action协议没有变化,只是增加了optional:
#import <Cocoa/Cocoa.h>
@protocol Action <NSObject>
@optional
- (void) doAction;@end
Screen类,可以看作抽象类,它主要供继承使用,来复用委托模式的代码。头文件:
#import <Foundation/Foundation.h>
#import "Action.h"@interface Screen : NSObject <Action> {
id <Action> delegate;
}@property(nonatomic,retain) id <Action> delegate;
- (void) onTouch;
@end
实现文件:
#import "Screen.h"
@implementation Screen
@synthesize delegate;
- (void) onTouch{
NSLog(@"on touch …");
if ([delegate conformsToProtocol:@protocol(Action)] &&
[delegate respondsToSelector:@selector(doAction)]) {
[delegate performSelector:@selector(doAction)];
}
NSLog(@"on touched.");
}@end
这里不再实现doAction方法。
下面看MyScreen类的头文件:
#import <Cocoa/Cocoa.h>
#import "Screen.h"@interface MyScreen : Screen {
}
@end
MyScreen类的实现文件:
#import "MyScreen.h"
@implementation MyScreen
- (id) init{
if (self=[super init]) {
delegate=self;
}
return self;
}- (void) doAction{
NSLog(@"Bang!!!!!!!!!");
}@end
用类别实现委托模式
可以使用类别(Category)实现委托模式。还是上面的例子。下面使用Category实现了个示例。
main方法:
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];Screen *screen=[[Screen alloc] init];
[screen onTouch];
[screen release];
[pool drain];
return 0;
}
Screen类的头文件:
#import <Foundation/Foundation.h>
@interface Screen : NSObject {
id delegate;
}@property(nonatomic,retain) id delegate;
- (void) onTouch;
@end
在这个示例中,实际上property没有起什么作用。
实现文件:
#import "Screen.h"
@implementation Screen
@synthesize delegate;
- (id) init{
if (self=[super init]) {
delegate=self;
}
return self;
}- (void) onTouch{
NSLog(@"on touch …");
if ([delegate respondsToSelector:@selector(doAction)]) {
[delegate performSelector:@selector(doAction)];
}
NSLog(@"on touched.");
}@end
写到这里,如果运行代码,只会打印类似下面的日志:
2011-05-26 10:37:30.843 DelegateDemo[5853:a0f] on touch …
2011-05-26 10:37:30.846 DelegateDemo[5853:a0f] on touched.
下面写Category代码,名称为ScreenAction,它的头文件:
#import <Cocoa/Cocoa.h>
#import "Screen.h"@interface Screen (ScreenAction)
- (void) doAction;
@end
实现文件:
#import "ScreenAction.h"
@implementation Screen (ScreenAction)
- (void) doAction{
NSLog(@"BANG!!!!!!");
}@end
实现了这部分代码再执行:
2011-05-26 10:37:30.843 DelegateDemo[5853:a0f] on touch …
2011-05-26 10:37:30.846 DelegateDemo[5853:a0f] BANG!!!!!!
2011-05-26 10:37:30.846 DelegateDemo[5853:a0f] on touched.
ObjC: 委托模式的更多相关文章
- 用java语言实现事件委托模式
http://blog.csdn.net/yanshujun/article/details/6494447 用java语言实现事件委托模式 2010-04-27 00:04 2206人阅读 评论(1 ...
- iOS中常见的设计模式——单例模式\委托模式\观察者模式\MVC模式
一.单例模式 1. 什么是单例模式? 在iOS应用的生命周期中,某个类只有一个实例. 2. 单例模式解决了什么问题? 想象一下,如果我们要读取文件配置信息,那么每次要读取,我们就要创建一个文件实例,然 ...
- ios专题 - 委托模式实现
在ios中,委托模式非常常见,那委托模式是什么? 委托模式是把一个对象把请求给另一个对象处理. 下面见例子: #import <UIKit/UIKit.h> @protocol LQIPe ...
- 设计模式--委托模式C++实现
原文章地址:http://www.cnblogs.com/zplutor/archive/2011/09/17/2179756.html [委托模式 C++实现] 我对.Net的委托模型印象很深刻,使 ...
- PHP设计模式之委托模式
委托模式: 通过分配或委托至其他对象,委托设计模式能够去除核心对象中的判决和复杂的功能性. class Bank{ protected $info; /* 设置基本信息 @param string $ ...
- IOS常用设计模式之委托模式
对于iOS开发,举例Cocoa框架下的几个设计模式为大家分析.当然,Cocoa框架下关于设计模式的内容远远不止这些,我们选择了常用的几种:单例模式.委托模式.观察者模式.MVC模式. 委托模式 委托模 ...
- classloader加载的双亲委托模式
要深入了解ClassLoader,首先就要知道ClassLoader是用来干什么的,顾名思义,它就是用来加载Class文件到JVM,以供程序使用 的.我们知道,java程序可以动态加载类定义,而这个动 ...
- [js高手之路]设计模式系列课程-委托模式实战微博发布功能
在实际开发中,经常需要为Dom元素绑定事件,如果页面上有4个li元素,点击对应的li,弹出对应的li内容,怎么做呢?是不是很简单? 大多数人的做法都是:获取元素,绑定事件 <ul> < ...
- 再起航,我的学习笔记之JavaScript设计模式28(委托模式)
## 委托模式 ### 概念介绍 **委托模式(Entrust): **多个对象接收并处理同一请求,他们将请求委托给另一个对象统一处理请求. ### 利用委托优化循环 如果我们有一个需求需要让用户点击 ...
随机推荐
- idea中的插件,可以快速将类中的属性转换成Json字符串
当我们想要测试接口的时候,难免会根据一个类,一个一个的写json数据,当属性比较少时还行,但当属性多的时候就比较麻烦了, 为了解决这个问题,我们可以安装第三方的插件来快速生成json字符串. 步骤如下 ...
- python 一些程序规范(跨目录调用文件)
文档内容学习于 http://www.cnblogs.com/xiaozhiqi/ 参考: https://www.cnblogs.com/monologuesmw/p/9490093.html 软件 ...
- 每天一道Rust-LeetCode(2019-06-05)
每天一道Rust-LeetCode(2019-06-05) 最长回文子串 坚持每天一道题,刷题学习Rust. 接续昨天,最长会问字符串的另一种解法 题目描述 解题过程 //leetcode最快解法 / ...
- 快速调用Android虚拟机
由于使用Android studio开发flutter 每次打开软件都需要很长时间,所以我自己使用vscode进行开发,没有虚拟机也开发不了,于是就有了这篇博客的作用啦 在本地桌面新建一个xxx.ba ...
- Apex 的 API 简介
Salesforce 数据API分类 Salesforce中提供了四种数据API接口.数据API接口为开发者提供了操作Salesforce数据的能力.它们分别是: REST API:依据RESTful ...
- MySQL中DELETE子句与TRUNCATE TABLE语句的区别
TRUNCATE语句删除表数据的语法格式:即,table 这个词可要,可不要 TRUNCATE TABLE 表名; TRUNCATE 表名; 1,使用truncate语句后,表中的auto_incre ...
- c# 笔试面试题01
一.抽象与接口的区别: ,抽象(abstract): ()抽象类中可以有抽象方法,也可没有: ()抽象方法包含实现,也可以由子类实现: ()抽象类不能被sealed修饰,只能使用abstract关键字 ...
- 【2019年07月22日】A股最便宜的股票
查看更多A股最便宜的股票:androidinvest.com/CNValueTop/ 便宜指数 = PE + PB + 股息 + ROE,四因子等权,数值越大代表越低估. 本策略只是根据最新的数据来选 ...
- VUE引入jq bootstrap 之终极解决方案(测试)
初入VUE遇见的一些问题,在网上找了些方法,再根据自己的实际项目解决的问题写得此文,,希望对你有所帮助. vue-cli快速构建项目以及引入boostrap.jq各种插件配置 vue-cli脚手架工具 ...
- linux-centos安装图解及配置IP远程连接
本次安装使用vm软件的15版本,系统为centos7.6(1810) 系统安装图解>配置IP信息联网>真实机是无线网络状态,虚拟机如何联网>远程工具连接虚拟机 一,vm安装cento ...