代理(Delegate)、闭包(Closure)、Notification(通知中心) 和 swift_event_bus适用场景和工作方式

发布于:2025-03-12 ⋅ 阅读:(15) ⋅ 点赞:(0)

在 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 处理全局事件(如登录成功、歌曲切换)。