从Delegate到DelegateBlock

2017-08-04

从Delegate到DelegateBlock

在iOS开发中常与各种delegate打交道,这种代理委托协议的开发模式学习成本很低且扩展性特别强,本文就将在delegate开发模式的基础上进一步扩展它,从Delegate转换到DelegateBlock的模式。

Alt text

用一张图来展示这种Delegate转换到DelegateBlock的对比:
Alt text

改造前,实现协议的步骤如下:

  1. 在类名后标记协议名。
  2. 在类中添加并实现委托协议方法。

扩展后,实现协议的步骤如下:

  1. 创建一个DelegateBlock代理对象。
  2. 代理对象绑定委托协议selector与block。

从上述可看出改造前后有一点很大的区别:改造后的委托协议绑定方法是可以替换的,而改造前在类中添加方法的这种就只能用继承或者动态替换方法的办法来替换对应的逻辑的,什么?没看懂?举个栗子:你写了一个代理对应的若干委托方法实现,该段代码将在项目内很多地方被使用,但是有几处将会在某一个或几个委托方法有完全不一样的处理逻辑,如果用传统的delegate处理逻辑,那就需要继承类重写委托方法了,如果有一个对象,能和设置属性一样方便的设置一个block与select的mapping关系,当委托方法被调用时,替换成执行block,那就能轻松解决这个代码重用问题。

什么?还是没看懂?用代码吧:
Alt text

PS:WtDelegateProxy类的灵感来至于RACDelegateProxy。

WtDelegateProxy类只支持一个协议(代码结构更清晰),它有一个绑定selector与block的方法,当绑定完后,delegate调用selector时,会触发消息转发行为,将在内部执行block方法,从而实现delegate到delegateBlock的转换。

流程如图所示:
Alt text

它能干什么呢?
先来看一下iOS端开发最为常见的一个UI组建:UITableView,UITableView是Apple提供的UI滚动单元格控件,可以说是iOS开发中使用频率最高的控件,日常使用的APP诸如QQ、微信、网易新闻等软件中大量使用了该控件,它能给用户带来极佳的用户体验。

UITableView的性能极高,提供给开发者的API也非常容易上手,它提供了UITableViewDataSource和UITableViewDelegate两个代理类,前者是数据源代理,UITableView会从它委托的代理方法中获取数据,后者是一般代理,UITableView的生命周期事件以及交互事件等都会回调到它委托的代理方法。

通常UITableView中只有一种Cell类型的时候,代码会特别的简洁和清晰,一旦Cell类型巨增的时候,委托代理方法中的逻辑将变得十分可怕,看看如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
if (indexPath.section == 0) {
if (indexPath.row == 0) {
...
}else if (indexPath.row == 1) {
...
}else {
...
}
}else if (indexPath.section == 1) {
...
}else {
...
}
return cell;
}

项目中这样的需求并不少见,就拿起点读书APP来说,诸如:精选页、书籍详情页、账户页。如果TableView中的cell出现组合策略的话,其中的逻辑会变的更加复杂,使用上述的这种判断indexPath的办法显得十分笨拙以及难以维护,甚至一不小心就会出现数据源与UI刷新不一致从而导致崩溃。

接下来一起来看看如何从容优雅地使用它来实现UITableView,改造后:
Alt text

流程如下:
Alt text

也就是将TableView的数据源由Model换为ViewModel,ViewModel只干一件事:双向数据绑定!UIViewController中的代理方法也只干一件事:取出ViewModel,然后调用对应的代理方法。

在ViewModel中绑定对应协议委托方法,这些方法有对应的Cell如何创建、多高、点击事件、3DTouch重压事件,该ViewModel可以提供给组内其他开发人员使用,只需要设置一些数据字段,就可以简单的展示在TableView上,如果你需要一个不一样的点击事件,可以将ViewModel中的delegate对象重新设置selector与block的绑定关系即可。

最后Demo地址奉上。