本章主要讲解委托模式以及通过委托模式实现的回调接口。
1.委托模式
委托模式是OC语法独有的开发模式。是基于组件拼装的一种快速开发模式。该模式下,可以保证组件的高度灵活性和通用性。属于组件的一种开放式接口。
下面通过一个现实生活中的场景简单理解下委托模式的应用。
例如我们现在有一个公司。公司想要进行IPO。可是公司老总并不熟悉资本操作,这时候就需要委托一个人或者一个机构来作这件事。
公司首先要提出能做IPO这件事的详细要求,然后通过猎头寻找合适的人选。
用代码描述应该是这样的:
- 首先用协议描述公司的用人要求
- 声明一个遵守该协议的属性,这个属性应该由外界赋值,比如猎头公司
- 在公司要上市的时候,本质上有遵守协议的onePerson来做这件事。到底谁是onePerson不重要,重要的是他有上市的方法。
//公司的用人协议
@protocol CompanyProtocol <NSObject>
-(void)doIPO;
@end
//公司类的声明
@interface Company : NSObject
@property(nonatomic,strong)id<CompanyProtocol>onePerson;
-(void)stratIPO;
@end
//公司类的实现
@implementation Company
-(void)stratIPO
{
if(self.onePerson)
{
[self.onePerson doIPO];
}
}
@end
公司的声明代码中有一个属性。这种模式和复合开发模式有些相似,但本质上不同。
我们拿之前通过复合模式开发的高级空调来分析。在准备要开发高级空调的时候,除甲醛的模块已经存在,所以直接声明一个除甲醛的属性并且在初始化的时候为这个属性分配内存,就可以让空调具有除甲醛的功能。
但在描述公司的时候,我们并不知道,谁能够带领公司进行IPO,但是又不能因为一个岗位的人选没有确定,而让整个公司停止运转。所以采用委托的方式,将这件事进行外包处理。公司不关心具体是哪个对象做的,但是关心这件事是否能完成。
下面我们来创建一个可以完成上市操作的对象,让其帮助公司完成上市任务。
@interface Finance : NSObject <CompanyProtocol>
@end
@implementation Finance
-(void)doIPO
{
NSLog(@"开始上市!");
}
@end
现在公司和能带领公司的人都齐全,就可以进行下一步开发了。
main()
{
Finance * per = [[Finance alloc] init];
Company * company = [[Company alloc] init];
company.onePerson = per;
[company startIPO]; // 当公司开始IPO时,本质上是调用Finance对象per的doIPO方法。
}
虽然从代码上面看,是公司进行IPO,但开发本质是公司委托per对象进行IPO操作。这样的模式就是委托模式。
2.委托回调
委托模式的实现较为复杂,且需要根据实际应用场景进行设计。但之后我们使用的并不多,在这里只做简单了解即可。
委托回调模式是基于委托模式基础上进行的事件反馈接口封装。
仍然拿上一章的开关作为讲解案例。上一章我们已经知道回调的概念,以及回调的意义。核心就是让组件的状态能够及时反馈到使用者。在目标动作模式下通过配置组件的回调对象和回调方法来监控组件的状态。
本章的委托回调模式,则是通过设置组件的委托人和实现委托方法来监控组件的状态。
委托(代理)人:从上文中公司上市的例子可以看出,公司委托财务人员进行上市操作。所以,从公司的角度看,财务人员是公司的委托人。从上市这个操作来说,财务人员是公司的代表,也称代理人。委托人和代理人本质上是一个意思,只是从不同角度对同一个对象的不同描述。
委托(代理)方法:公司通过协议来声明委托人的行为。作为公司的委托人,当然必须具有这些行为。协议中声明的方法从公司角度说是委托方法,从财务人员角度来说是代理方法。
通常组件需要通过委托方法反馈自身状态信息。使用者如果想监控组件状态,只需作为组件的委托人,并且实现委托方法即可。
下面我们用开关做例子进行代码的讲解。
- 阅读开关的声明文件,确定委托人属性以及委托方法
- 将开关作为房间的组件,声明属性并分配内存
- 设置开关的委托人并实现委托方法
开关的声明文件
@protocol SwitchDelegate;//前向声明,告知编译器SwitchDelegate为一个协议
@interface SwitchD : NSObject
typedef enum : NSUInteger {
SwitchStateOff,//default
SwitchStateOn,
} SwitchState;
@property(nonatomic,assign,readonly)SwitchState currentState;
@property(nonatomic,weak)id<SwitchDelegate>delegate;
@end
@protocol SwitchDelegate <NSObject>
//当开关状态改变时,会调用委托人的此方法进行状态反馈。
-(void)switchD:(SwitchD *)s didChangeState:(SwitchState)currentState;
@end
房间的声明文件.h
@interface Room : NSObject <SwitchDelegate>
@end
房间的实现文件.m
@interface Room ()<SwitchDelegate>
@property (strong, nonatomic) Light *aLight;
@property (strong, nonatomic) SwitchD *aSwitch;
@end
@implementation Room
- (instancetype)init
{
self = [super init];
if (self)
{
self.aLight = [[Light alloc] init];
self.aSwitch = [[SwitchD alloc] init];
//设置开关的委托人为自己(Room对象)
self.aSwitch.delegate = self;
}
return self;
}
- (void)switchD:(SwitchD *)s didChangeState:(SwitchState)currentState
{
if (currentState == SwitchStateOff)
{
[self.aLight turnOff];
}
else
{
[self.aLight turnOn];
}
}
@end
通过上述代码,可以看出,当开关状态改变的时候,开关对象会调用Room实现的委托方法。通过在委托方法内部的代码,实现对开关状态的反馈进行处理。
3.委托回调与目标-动作回调
我们现在已经接触两种回调模式,这里讲解一下其中的不同和使用场景。
目标-动作回调属于简单组件的状态回调。其特点是灵活,用户可以自己制定回调方法,但同时这也是目标动作回调的劣势。由于可以自己指定回调方法,在回调时便无法对回调参数给出明确的声明。所以,目标动作回调通常带有一个参数,即触发回调方法的对象本身,其他参数需要通过对象本身的属性继续获取。
委托模式回调属于复杂组件回调。其特点是回调方法提前被协议声明好了。所以可以规定其回调参数,适合携带参数的事件回调。在App开发中,一般的事件都会携带参数,所以,具有委托回调模式接口的组件更加常见。
但委托回调也有自己的不足之处,就是如果一个类内同时有两个相同类型的组件。那么这两个组件的回调方法也是相同的,我们就需要在方法内进行判断,是哪一个组件回调了委托方法。无法实现每个组件的独立回调。在下一章的代码块回调中,这个问题被完美解决。