Swift 5中单个类持有多个delegate是否合理?有何优缺点?
同一个类中声明持有多个弱引用delegate是完全合规合理的Swift实现方式,不存在语法或设计原则上的错误,是否适用完全取决于具体业务场景
你给出的示例代码本身在内存安全层面是没有问题的:两个协议都约束了AnyObject(仅允许类类型实现),delegate属性都用weak修饰,不会产生循环引用。唯一的问题是delegate1、delegate2的命名完全没有语义,实际开发中要替换成能体现职责的命名。
适用场景
当一个类需要向外部分发不同职责维度的事件时,拆分多个delegate是非常符合设计原则的做法。举个最常见的例子:系统的UITextField本身就同时持有delegate(负责编辑行为、文本变化回调)和输入辅助项相关的多个委托引用,很多音视频播放器、列表组件也会拆分多个delegate处理不同领域的回调:
playbackDelegate只负责播放状态相关回调(播放/暂停/进度更新/播放结束)renderDelegate只负责画面渲染相关回调(首帧渲染/分辨率切换/渲染卡顿)networkDelegate只负责网络相关回调(缓冲状态/拉流错误/地址过期)
这种场景下如果把所有回调塞到同一个臃肿的delegate协议里,反而会让实现方被迫依赖一堆和自身职责无关的接口。
写法优势
- 完全符合接口隔离原则:不同职责的回调拆分到独立的小协议中,调用方只需要实现自己关心的协议即可,不需要依赖包含几十上百个方法的大而全的协议
- 内存安全可控:只要遵循「协议加AnyObject约束、delegate属性用weak修饰」的规则,多delegate写法和传统单delegate写法的内存安全性完全一致,不会出现循环引用
- 职责边界清晰:语义化命名的delegate属性能直接体现对应回调的负责领域,代码可读性更高,后续维护时不需要在冗长的大协议里翻找对应逻辑
- 配置灵活度高:可以针对不同delegate单独配置回调派发队列、回调策略,比如网络相关回调派发到后台队列,UI相关的播放状态回调固定派发到主队列,互不干扰
存在的弊端
- 拆分层级不合理时会提升使用成本:如果为了拆而拆,把关联度极高的逻辑拆成五六个delegate,外部初始化这个类的时候需要逐个给delegate属性赋值,很容易出现遗漏,使用体验很差
- 无法替代多播委托场景:注意这种每个delegate属性单独持有一个引用的写法,和「一个事件通知多个观察者」的多播委托是完全不同的设计。如果同一个事件需要同时发给多个对象,这种写法实现不了,应该选择通知中心、Combine或者自定义多播委托容器
- 容易出现职责混乱:如果拆分的时候没有划清不同delegate的职责边界,导致多个协议里出现大量功能重叠的回调方法,反而会让逻辑变得混乱,调用方也不知道该实现哪个协议才对
编写注意事项
- 所有作为delegate的协议必须加上
AnyObject约束,否则weak修饰符会直接编译报错,同时也能避免值类型实现协议带来的预期外行为 - 所有delegate属性必须用
weak修饰,否则一定会产生循环引用 - 不要用
delegate1、delegate2这种无意义命名,delegate的属性名要直接体现它负责的职责域 - 如果多个delegate对应的回调职责高度重叠,不要强行拆分,合并到同一个协议里反而更易维护
内容的提问来源于stack exchange,提问作者SadDeveloper
相关产品推荐
相关产品推荐

