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