在 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 = selfplayer.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 inprint("播放完成")
}// 發送事件
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 = selfplayer.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 inprint("播放完成")
}// 發送事件
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 處理全局事件(如登錄成功、歌曲切換)。