Block、委托模式、Notifcation三种模式的抉择

54 阅读4分钟

如何选择Block、Delegate、Notification首先得分析各自的自责和优劣势。

Block

Block是Apple在C、C++和Objective-C中引入的一种语言特性,允许创建可以捕获周围作用域变量的匿名函数(闭包)。

适用场景:

  • 一对一的简单回调:特别是异步操作完成后需要执行的逻辑,例如网络请求回调、文件读写完成回调等。
  • 回调逻辑简单且代码进紧凑:当回调代码量少,且与触发点代码上下文紧密相关时,使用Block可以保持代码的连贯性。
  • 需要捕获上下文:Block可以方便地捕获当前作用域内的变量,适合需要访问当前环境数据的场景。
  • 链式调用:Block常用于链式编程,使代码可读性更高。

优势:

  • 代码内聚:回调逻辑写在调用点附近,便于阅读。
  • 灵活捕获上下文:自动捕获变量,减少额外代码。
  • 轻量:不需要定义额外的协议或方法。

缺点:

  • 容易引起循环引用:如果Block内部捕获了self,而self又持有Block,则会导致循环引用。(需使用week解决)
  • 嵌套过审可读性差:多层Block嵌套会使代码难以阅读。
  • 不适用于复杂回调:多个回调或多种事件类型使用Block会导致代码臃肿。

委托模式

委托模式是一种非常常用的设计模式,主要用于对象之间的通信。它允许一个对象将某些任务或决定交给另一个对象来处理。

适用场景:

  • 一对一的复杂交互:当对象需要向外部传递多种事件,例如UITableViewDataSource、UITableViewDelegate。
  • 需要高度定制:委托对象可以根据协议提供不同的实现,适合需要灵活扩展的场景。
  • 跨多个类型共享行为:多个对象可以实现同一个协议,复用相同的处理逻辑。
  • 生命周期事件:视图控制器的生命周期通常使用Delegate(viewDidLoad、viewDidAppear)

优点:

  • 职责分离:将事件处理逻辑分离到独立的委托对象中,符合单一职责原则。

  • 支持多种事件:通过定义协议方法,可以处理多种不同的回调事件。

  • 避免循环引用:通常delegate使用weak引用,不易引起循环引用。

  • 可复用性:同一委托对象可以服务于多个同类组件。

缺点:

  • 代码分散:回调逻辑分散在委托对象的不同方法中,与触发点分离。

  • 模版代码多:需要定义协议、实现多个方法,代码量较大。

  • 只能一对一:一个对象通常只能设置一个delegate。

Notification(通知)

Notification模式是一种消息传递机制,用于在不同对象之间进行解耦的通信。主要基于观察者模式实现。

适用场景:

  • 一对多的广播:一个事件需要通知多个无关的对象。

  • 跨层通信:从深层子控件直接通知到顶层的控制器,避免层层传递。

  • 模块间松耦合:通信双方不需要直接引用,通过通知中心进行交互,降低耦合度。

  • 系统事件监听:监听键盘弹出/收起、应用进入后台等系统事件。

优势:

  • 完全解耦:发送者和接收者不需要知道对方的存在。

  • 支持多接受者:一个通知可以同时被多个对象响应。

  • 适用于全局事件:适合传递全局状态变化。

缺点:

  • 类型不安全:通知使用字符串作为标识,容易拼写错误且编译器无法检查。
  • 调试困难:多个地方响应同一个通知时,调试流程较复杂。
  • 无法直接返回值:通知是单向广播,不支持接收者返回值给发送者。
  • 生命周期管理:注册通知后需要手动移除,否则可能引起崩溃。

综合对比表

特性Block委托模式Notification
通信方向一对一一对一一对多
耦合度较高(直接引用)低(通信协议)极低(完全解耦)
代码内聚性高(回调与触发点在一起)低(分散在代理方法中)低(分散在多个观察者中)
适用场景简单回调、异步结果复杂交互、多事件回调广播、全局事件
循环引用风险高(需weak打破)低(delegate通常为weak)低(但需要注意移除观察者)
典型应用请求回调、动画完成回调UITableView事件处理全局状态变化(登录/登出)