协议(Protocol) 和代理(Delegate)
1、概念与组成
delegate是iOS中一种常见的设计模式,是一种消息传递的的方式,常见的消息传递方式还有以下几种:
通知:在iOS中由通知中心进行消息接收和消息广播,是一种一对多的消息传递方式。
代理:是一种通用的设计模式,iOS中对代理支持的很好,由代理对象、委托者、协议三部分组成。
block:iOS4.0中引入的一种回调方法,可以将回调处理代码直接写在block代码块中,看起来逻辑清晰代码整齐。
target action:通过将对象传递到另一个类中,在另一个类中将该对象当做target的方式,来调用该对象方法,从内存角度来说和代理类似。
KVO:NSObject的Category-NSKeyValueObserving,通过属性监听的方式来监测某个值的变化,当值发生变化时调用KVO的回调方法。
我们可以通过一个简单的例子来解释什么是代理?什么是协议?
有个baby不会自己吃饭和洗澡等等做一些事情,于是baby就请了一个保姆,于是baby和保姆之间有了一个协议(Protocol)合同,协议合同中写明了保姆需要做什么事情, 而保姆就是要去完成这个协议中规定要做的事的代理人。
即:baby和保姆之间有个协议,保姆遵守该协议,于是保姆就需要实现该协议中的条款成为baby代理(delegate)人,而baby就是保姆的委托方。
说白了,代理的作用大家可以简单粗暴的理解为:"自己做不了的事情,就去雇佣一个可以做这些事的人,交给他去做!"
所以,我们从上面这个关系中可以看出,代理设计到了三个东西:委托方、代理方、协议,三者关系如下图所示↓↓↓
协议:用来指定代理双方可以做什么,必须做什么。
代理:根据指定的协议,完成委托方需要实现的功能。
委托:根据指定的协议,指定代理去完成什么功能。
2、协议
在实际应用中通过协议来规定代理双方的行为,协议中的内容一般都是方法列表,当代理方成为委托方代理并遵守相关协议后,委托方可以让代理方执行协议中规定的操作。


协议有两个修饰符@optional和@required,创建一个协议如果没有声明,默认是@required状态(必须实现)的。这两个修饰符只是约定代理是否强制需要遵守协议,如果@required状态的方法代理没有遵守,会报一个黄色的警告,只是起一个约束的作用,没有其他功能。【@required是需要我们必须实现的(不实现也只是报个黄色警告而已)。@optional是可以选择实现的.】
无论是@optional还是@required,在委托方调用代理方法时都需要做一个判断,判断代理是否实现当前方法,否则会导致崩溃。
示例:// 判断代理对象是否实现这个方法,没有实现会导致崩溃
if([self.delegate respondsToSelector:@selector(userLoginWithUsername:password:)]) {
[self.delegate userLoginWithUsername:self.username.text password:self.password.text];
}
3、实际演练
项目中有两个控制器,CDMyAddressListController(以下简称List)是用户地址列表控制器,CDAddAddressController(以下简称add)用户添加/删除地址的控制器,CDAddAddressController是从CDMyAddressListController push出来的
现在的需求是,当用户在CDAddAddressController删除地址后,要CDMyAddressListController中展现的是最新的数据:
这里可以用到的方法很多,比如说在删除地址成功后发送通知,CDMyAddressListController接收到通知后做刷新处理,或者最笨的方法就是每次进入地址列表页都进行刷新,当然,我们也可以通过代理来实现:
首先,分析谁是委托方,谁是代理方:
委托方通过协议来让代理方做事情的,而在这个项目中,add控制器想要在特定时候让List控制器去刷新,所以add控制器就是委托方,而List控制器就是代理方。
接下来,我们来看协议:
因为这个协议只是在add这个类中会用到,所以写在add类内部就可以,不用再创建pertocol文件存放了;
这个协议只规定了一件事情,那就是刷新数据,而且当委托方发出需求是,代理方必须要实现,所以是@required状态
//
// CDAddAddressController.h
// xx
//
// Created by xx on 2019/4/11.
// Copyright © 2019 xx. All rights reserved.
// #import <UIKit/UIKit.h>
@class CDMyAddressListModel;
//制定协议
@protocol CDAddAddressControllerDelegate <NSObject>
- (void)reloadDataToRefresh;
@end @interface CDAddAddressController : UIViewController
//使用协议修饰属性,声明当前属性时已经遵守协议的,当代理方出现了该协议中的方法时,编译器就不会报错
//delegate属性就是建立委托方和代理方的链接,→→ @property(nonatomic,weak) id delegate;
@property(nonatomic,weak) id<CDAddAddressControllerDelegate> delegate;
@property(nonatomic,strong) CDMyAddressListModel *model;
@end
制定完协议后,当委托方需要的时候就可以指定代理方做事了:
CDAddAddressController.m //删除地址
- (void)delete{
// 初始化对话框
UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:@"是否确认删除改地址?" preferredStyle:UIAlertControllerStyleAlert];
// 确定注销
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction *_Nonnull action) {
[[CDWebService instance]deleteMyAddressWithParameters:@{@"address_id":self.model.address_id} success:^(id json) {
if ([json[@"code"]integerValue] == ) {
[CDCommentAlertView showRequestStatus:statusNormal explain:@"删除成功"];
//要求代理方执行协议中的方法
// 判断代理方法是否存在
if ([self.delegate respondsToSelector:@selector(reloadDataToRefresh)]) {
[self.delegate reloadDataToRefresh];
}
[self .navigationController popViewControllerAnimated:YES];
}else{
[CDCommentAlertView showRequestStatus:statusNormal explain:json[@"message"]];
}
} failure:^(NSError *error) {
[CDUtils alertError:error];
}]; }];
UIAlertAction *cancelAction =[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil];
[alert addAction:okAction];
[alert addAction:cancelAction];
[self presentViewController:alert animated:true completion:nil];
}
而代理方现在需要做的就是成为代理、遵守协议、实现代理方法
CDMyAddressListController.m
//在声明部分遵守协议< CDAddAddressControllerDelegate > //添加/删除地址
- (void)addAddress{
CDAddAddressController *add = [[CDAddAddressController alloc]init];
add.delegate = self;
[self.navigationController pushViewController:add animated: YES];
} #warning CDAddAddressControllerDelegate
//通过代理来
- (void)reloadDataToRefresh{
[self loadNewData];
}
至此,一个完整的代理就完成了。
4、实现原理
其实代理的实现没有涉及到什么底层的东西,只不过是代理对象内存的传递和操作。
我们在委托类设置代理对象后,实际上只是用一个id类型的指针将代理对象进行了一个弱引用。委托方让代理方执行操作,实际上是在委托类中向这个id类型指针指向的对象发送消息,而这个id类型指针指向的对象,就是代理对象。
按上面例子来说的话,就是
CDAddAddressController *add = [[CDAddAddressController alloc]init];
//self就是list对象,self设置成add控制器的代理,实际上也就是成为add控制器的一个属性而已
add.delegate = self; [self.navigationController pushViewController:add animated: YES];
//要求代理方执行协议中的方法
// 判断代理方法是否存在
//delegate这个属性本身存放的就是list控制器这个对象,检测代理方法是否存在也就是相当与查看list对象中是否存在这个方法
if ([self.delegate respondsToSelector:@selector(reloadDataToRefresh)]) {
//相当于 [list reloadDataToRefresh]; 也就是list对象调用自己的方法
[self.delegate reloadDataToRefresh];
}
5、为什么用weak修饰delegate属性
为了避免循环引用
首先我们要知道我们在创建对象的时候如果没有特别说明的话我们默认的是strong强引用(在OC中,对象默认都是强指针),所以我们在list控制器中创建add对象后,list对象使其对add对象有一个强引用,
而add中的delegate其实就是引用的list对象,如果用strong来修饰的话,那么这两个引用都是强引用,就会出现循环引用的问题,导致双方都没办法去释放:
所以我们需要用weak来修饰delegate属性,这样add属性对list是一个弱引用,list对add是强引用,这样就避免了循环引用
参考资料
协议(Protocol) 和代理(Delegate)的更多相关文章
- 浅谈iOS开发的协议(protocol)和代理(delegate)
协议和代理对于一个新手来说确实不讨好理解,也有很多的iOS开发的老手对此是懂非懂的.网上的很多博文只是讲了怎么使用,并没有说的很明白.下面我谈一下我的理解. 1.你要先搞明白,协议和代理为什么会出现, ...
- 窥探Swift之协议(Protocol)和委托代理(Delegate)回调的使用
协议与委托代理回调在之前的博客中也是经常提到和用到的在<Objective-C中的委托(代理)模式>和<iOS开发之窥探UICollectionViewController(四) - ...
- 协议(Protocol)与委托代理(Delegate)
协议(Protocol)的作用: 1. 规范接口,用来定义一套公用的接口: 2. 约束或筛选对象. 代理(Delegate): 它本身是一种设计模式,委托一个对象<遵守协议>去做某件事情, ...
- 【转】iOS开发-Protocol协议及委托代理(Delegate)传值
原文网址:http://www.cnblogs.com/GarveyCalvin/p/4210828.html 前言:因为Object-C是不支持多继承的,所以很多时候都是用Protocol(协议)来 ...
- iOS开发-Protocol协议及委托代理(Delegate)传值
前言:因为Object-C是不支持多继承的,所以很多时候都是用Protocol(协议)来代替.Protocol(协议)只能定义公用的一套接口,但不能提供具体的实现方法.也就是说,它只告诉你要做什么,但 ...
- 协议Protocol
1.协议:是一组声明方法的集合,不能声明成员变量,作用类似于接口. 遵守此协议的类就相当于拥有了这个协议的所有方法的声明,如果父类遵守了某个协议,子类也遵守了这个协议. ...
- iOS 的一点理解(一) 代理delegate
做了一年的iOS,想记录自己对知识点的一点理解. 第一篇,想记录一下iOS中delegate(委托,也有人称作代理)的理解吧. 故名思议,delegate就是代理的含义, 一件事情自己不方便做,然后交 ...
- swift -- 代理delegate
1.声明协议 protocol SecondDelagate { func sendValue(text : String!) -> Void } 2.声明代理属性 var delegate : ...
- 代理delegate
1>代理的用处是什么? 监听那些不能通过addTarget监听的事件 主要用开负责在两个对象之间,发生某些事件时,传递或发送消息 当我们需要 监听某些事件时,但苹果没有提供相关监听方法(addt ...
随机推荐
- Python time & random模块
time模块 三种时间表示 在Python中,通常有这几种方式来表示时间: 时间戳(timestamp) : 通常来说,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的 ...
- Python网络请求urllib和urllib3详解
Python网络请求urllib和urllib3详解 urllib是Python中请求url连接的官方标准库,在Python2中主要为urllib和urllib2,在Python3中整合成了urlli ...
- 改进MySQL Order By Rand()的低效率
Author:flymorn Source:飘易Categories:PHP编程 PostTime:2011-1-14 15:35:07 正 文: 最近由于需要研究了一下MYSQL的随机抽取实现方法. ...
- RDS for MySQL查询缓存 (Query Cache) 的设置和使用
https://help.aliyun.com/knowledge_detail/41717.html?spm=5176.7841698.2.11.aCvOXJ RDS for MySQL查询缓存 ( ...
- Oracle EBS LOV速度优化
一.现象 本文地址:http://blog.csdn.net/sunansheng/article/details/50952758 当我们的EBS LOV的SQL写得比較复杂.或者数据量比較多时,L ...
- poj 1256 Anagram—next_permutation的神奇应用
题意:给你一条字符串,让你输出字符串中字符的全排列,输出的顺序要按它给的奇葩的字典序. 题解:要输出全排列,暴力dfs可以过,但要注意题目的字典序以及相同字符的情况.如果用next_permutati ...
- Lightoj 1235 - Coin Change (IV) 【二分】
题目链接:http://www.lightoj.com/volume_showproblem.php?problem=1235 题意: 有N个硬币(N<=18).问是否能在每一个硬币使用不超过两 ...
- HDU 2110-Crisis of HDU(母函数)
Crisis of HDU Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) To ...
- 学习笔记——DISTINCT
DISTINCT印象中向来被人诟病,说它效率低下.但网上那些SQL 面试题答案,却时有用之.其中 COUNT(DISTINCT 句式,我以前很少用,这里做个笔记. 为管理岗位业务培训信息,建立3个表: ...
- Android将图像转换成流存储与将流转换成图像
1.将图片转换成二进制流 public byte[] getBitmapByte(Bitmap bitmap){ ByteArrayOutputStream out = new ByteArrayOu ...