代理(Delegate)、闭包(Closure)、Notification(通知中心) 和 swift_event_bus适用场景和工作方式
在 Swift 开发中,在 Swift 开发中,代理(Delegate)、闭包(Closure)、Notification(通知中心) 和 swift_event_bus 主要用于 组件之间的通信,但它们的适用场景和工作方式有所不同。以下是它们的区别:
1. 代理(Delegate)
 • 适用场景:适用于 一对一 通信,通常用于传递事件或回调,例如 UITableViewDelegate。
 • 实现方式:使用 协议(Protocol) 定义代理方法,调用方实现代理协议并传递代理对象。
 • 特点:
 • 强类型检查,编译时安全。
 • 适用于长期存在的关系(如 UITableView 和 UITableViewDelegate)。
 • 需要显式声明代理,并手动设置代理对象。
代码示例:
protocol PlayerDelegate: AnyObject {
    func didFinishPlaying()
}
class MusicPlayer {
    weak var delegate: PlayerDelegate?
    func play() {
        // 模拟播放完成
        delegate?.didFinishPlaying()
    }
}
class ViewController: PlayerDelegate {
    let player = MusicPlayer()
    init() {
        player.delegate = self
        player.play()
    }
    func didFinishPlaying() {
        print("播放完成")
    }
}
 
一个 protocol 可以在多个控制器中实现,这正是 Swift 协议的强大之处。协议(protocol)定义了一组方法和属性,任何类、结构体或枚举都可以遵循并实现这个协议,这允许你在多个 UIViewController 中实现相同的协议逻辑。
示例:一个协议在多个控制器中实现
假设你的 myMusic 项目中有一个播放器,你希望不同的控制器(比如 HomeViewController 和 PlaylistViewController)都能够监听播放器的播放完成事件,你可以这样做:
1. 定义一个协议
protocol MusicPlayerDelegate: AnyObject {
    func didFinishPlaying(song: String)
}
 
2. 在多个控制器中实现协议
第一个控制器:HomeViewController
class HomeViewController: UIViewController, MusicPlayerDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()
        MusicPlayer.shared.delegate = self
    }
    func didFinishPlaying(song: String) {
        print("HomeViewController: \(song) 播放完成,更新 UI")
    }
}
 
第二个控制器:PlaylistViewController
class PlaylistViewController: UIViewController, MusicPlayerDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()
        MusicPlayer.shared.delegate = self
    }
    func didFinishPlaying(song: String) {
        print("PlaylistViewController: \(song) 播放完成,刷新播放列表")
    }
}
 
问题:协议的 delegate 只能有一个对象
上面代码的 问题 是 MusicPlayer.shared.delegate 只能存储 一个 代理对象(即最后设置的那个),所以如果 PlaylistViewController 先设置了代理,而 HomeViewController 后设置代理,就会覆盖之前的代理。
解决方案 1:使用数组存储多个代理
如果你希望 多个控制器 都能监听播放器事件,可以改进 MusicPlayer,让它支持多个代理:
class MusicPlayer {
    static let shared = MusicPlayer()
    private var delegates = [MusicPlayerDelegate]()
    func addDelegate(_ delegate: MusicPlayerDelegate) {
        delegates.append(delegate)
    }
    func removeDelegate(_ delegate: MusicPlayerDelegate) {
        delegates.removeAll { $0 === delegate }
    }
    func play() {
        // 模拟播放完成
        notifyDelegates(song: "My Favorite Song")
    }
    private func notifyDelegates(song: String) {
        for delegate in delegates {
            delegate.didFinishPlaying(song: song)
        }
    }
}
 
使用方式
override func viewDidLoad() {
    super.viewDidLoad()
    MusicPlayer.shared.addDelegate(self)
}
 
解决方案 2:使用 NotificationCenter(适用于全局事件)
如果你不想手动管理多个代理数组,可以用 NotificationCenter 代替:
class MusicPlayer {
    static let shared = MusicPlayer()
    func play() {
        // 模拟播放完成,发送通知
        NotificationCenter.default.post(name: .musicFinished, object: nil, userInfo: ["song": "My Favorite Song"])
    }
}
extension Notification.Name {
    static let musicFinished = Notification.Name("musicFinished")
}
 
在多个控制器中监听
NotificationCenter.default.addObserver(self, selector: #selector(musicFinished(_:)), name: .musicFinished, object: nil)
@objc func musicFinished(_ notification: Notification) {
    if let song = notification.userInfo?["song"] as? String {
        print("收到通知:\(song) 播放完成")
    }
}
 
总结
| 方法 | 适用场景 | 优点 | 缺点 | 
|---|---|---|---|
| Delegate 单个对象 | 一对一通信 | 类型安全,编译时检查 | 只能有一个代理 | 
| Delegate 多个对象 | 多个对象监听 | 所有监听者都能收到回调 | 需要手动管理代理数组 | 
| NotificationCenter | 全局事件 | 解耦,不限监听者数量 | 运行时检查,代码可读性略低 | 
如果希望 多个控制器 监听同一事件:
 • 需要 强类型回调 → 用 多个 delegate(数组方式)
 • 需要 全局广播 → 用 NotificationCenter
2. 闭包(Closure)
 • 适用场景:适用于 一对一 或 临时性 事件回调,例如网络请求完成后回调。
 • 实现方式:将 函数作为参数 传递,通常用于短生命周期的任务,如异步操作或 UI 交互。
 • 特点:
 • 代码简洁,适合短期任务。
 • 避免定义额外的协议和类。
 • 容易导致循环引用(需注意 [weak self])。
代码示例:
class MusicPlayer {
    var onFinishPlaying: (() -> Void)?
    func play() {
        // 模拟播放完成
        onFinishPlaying?()
    }
}
let player = MusicPlayer()
player.onFinishPlaying = {
    print("播放完成")
}
player.play()
 
3. Notification(通知中心)
 • 适用场景:适用于 一对多 通信,例如全局状态变化、系统广播(如键盘弹出)。
 • 实现方式:使用 NotificationCenter 发送和监听通知。
 • 特点:
 • 解耦性高,不需要直接引用发送者。
 • 适用于全局事件,如应用生命周期、网络状态变化等。
 • 可能导致代码难以跟踪,容易造成资源泄漏(监听后需移除)。
代码示例:
// 发送通知
NotificationCenter.default.post(name: NSNotification.Name("MusicFinished"), object: nil)
// 监听通知
NotificationCenter.default.addObserver(self, selector: #selector(musicFinished), name: NSNotification.Name("MusicFinished"), object: nil)
@objc func musicFinished() {
    print("播放完成")
}
 
4. swift_event_bus(事件总线)
 • 适用场景:适用于 一对多 或 多对多 事件传递,通常用于解耦模块间的通信。
 • 实现方式:通过 SwiftEventBus 发布和订阅事件。
 • 特点:
 • 类似 NotificationCenter,但更轻量级,支持异步和线程调度。
 • 适用于模块化架构,如 MVVM、VIPER。
 • 易用性比 NotificationCenter 更高,但不如 delegate 和 closure 类型安全。
代码示例:
import SwiftEventBus
// 订阅事件
SwiftEventBus.onMainThread(self, name: "MusicFinished") { result in
    print("播放完成")
}
// 发送事件
SwiftEventBus.post("MusicFinished")
 
总结对比
| 方式 | 适用场景 | 关系类型 | 适用范围 | 代码复杂度 | 解耦性 | 
|---|---|---|---|---|---|
| 代理(Delegate) | 长期一对一通信 | 一对一 | UI 组件、数据回调 | 高 | 低 | 
| 闭包(Closure) | 短期回调 | 一对一 | 异步任务、临时事件 | 低 | 低 | 
| Notification | 一对多通信 | 一对多 | 全局事件、广播 | 中 | 高 | 
| swift_event_bus | 模块间解耦 | 一对多/多对多 | 事件驱动架构 | 低 | 高 | 
选择建议
 • 一对一且长期的关系(如 UITableView)→ Delegate
 • 一对一且是短期任务(如异步回调)→ Closure
 • 一对多或全局事件(如 App 生命周期)→ NotificationCenter
 • 解耦多个模块的通信(如 MVVM)→ swift_event_bus
你目前的 myMusic 项目如果涉及 UI 组件间的通信,比如播放器状态更新,建议:
 • 使用 Delegate 处理播放器的状态回调。
 • 使用 Closure 处理网络请求回调。
 • 使用 NotificationCenter 或 swift_event_bus 处理全局事件(如登录成功、歌曲切换)。
主要用于 组件之间的通信,但它们的适用场景和工作方式有所不同。以下是它们的区别:
1. 代理(Delegate)
 • 适用场景:适用于 一对一 通信,通常用于传递事件或回调,例如 UITableViewDelegate。
 • 实现方式:使用 协议(Protocol) 定义代理方法,调用方实现代理协议并传递代理对象。
 • 特点:
 • 强类型检查,编译时安全。
 • 适用于长期存在的关系(如 UITableView 和 UITableViewDelegate)。
 • 需要显式声明代理,并手动设置代理对象。
代码示例:
protocol PlayerDelegate: AnyObject {
    func didFinishPlaying()
}
class MusicPlayer {
    weak var delegate: PlayerDelegate?
    func play() {
        // 模拟播放完成
        delegate?.didFinishPlaying()
    }
}
class ViewController: PlayerDelegate {
    let player = MusicPlayer()
    init() {
        player.delegate = self
        player.play()
    }
    func didFinishPlaying() {
        print("播放完成")
    }
}
 
2. 闭包(Closure)
 • 适用场景:适用于 一对一 或 临时性 事件回调,例如网络请求完成后回调。
 • 实现方式:将 函数作为参数 传递,通常用于短生命周期的任务,如异步操作或 UI 交互。
 • 特点:
 • 代码简洁,适合短期任务。
 • 避免定义额外的协议和类。
 • 容易导致循环引用(需注意 [weak self])。
代码示例:
class MusicPlayer {
    var onFinishPlaying: (() -> Void)?
    func play() {
        // 模拟播放完成
        onFinishPlaying?()
    }
}
let player = MusicPlayer()
player.onFinishPlaying = {
    print("播放完成")
}
player.play()
 
3. Notification(通知中心)
 • 适用场景:适用于 一对多 通信,例如全局状态变化、系统广播(如键盘弹出)。
 • 实现方式:使用 NotificationCenter 发送和监听通知。
 • 特点:
 • 解耦性高,不需要直接引用发送者。
 • 适用于全局事件,如应用生命周期、网络状态变化等。
 • 可能导致代码难以跟踪,容易造成资源泄漏(监听后需移除)。
代码示例:
// 发送通知
NotificationCenter.default.post(name: NSNotification.Name("MusicFinished"), object: nil)
// 监听通知
NotificationCenter.default.addObserver(self, selector: #selector(musicFinished), name: NSNotification.Name("MusicFinished"), object: nil)
@objc func musicFinished() {
    print("播放完成")
}
 
4. swift_event_bus(事件总线)
 • 适用场景:适用于 一对多 或 多对多 事件传递,通常用于解耦模块间的通信。
 • 实现方式:通过 SwiftEventBus 发布和订阅事件。
 • 特点:
 • 类似 NotificationCenter,但更轻量级,支持异步和线程调度。
 • 适用于模块化架构,如 MVVM、VIPER。
 • 易用性比 NotificationCenter 更高,但不如 delegate 和 closure 类型安全。
代码示例:
import SwiftEventBus
// 订阅事件
SwiftEventBus.onMainThread(self, name: "MusicFinished") { result in
    print("播放完成")
}
// 发送事件
SwiftEventBus.post("MusicFinished")
 
总结对比
| 方式 | 适用场景 | 关系类型 | 适用范围 | 代码复杂度 | 解耦性 | 
|---|---|---|---|---|---|
| 代理(Delegate) | 长期一对一通信 | 一对一 | UI 组件、数据回调 | 高 | 低 | 
| 闭包(Closure) | 短期回调 | 一对一 | 异步任务、临时事件 | 低 | 低 | 
| Notification | 一对多通信 | 一对多 | 全局事件、广播 | 中 | 高 | 
| swift_event_bus | 模块间解耦 | 一对多/多对多 | 事件驱动架构 | 低 | 高 | 
选择建议
 • 一对一且长期的关系(如 UITableView)→ Delegate
 • 一对一且是短期任务(如异步回调)→ Closure
 • 一对多或全局事件(如 App 生命周期)→ NotificationCenter
 • 解耦多个模块的通信(如 MVVM)→ swift_event_bus
你目前的 myMusic 项目如果涉及 UI 组件间的通信,比如播放器状态更新,建议:
 • 使用 Delegate 处理播放器的状态回调。
 • 使用 Closure 处理网络请求回调。
 • 使用 NotificationCenter 或 swift_event_bus 处理全局事件(如登录成功、歌曲切换)。
