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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/897878.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

设计模式--单例模式(Singleton)【Go】

引言 在设计模式中,单例模式(Singleton Pattern)是一种非常常见且实用的模式。它的核心思想是确保一个类只有一个实例,并提供一个全局访问点。这种模式在需要全局唯一对象的场景中非常有用,比如配置管理、日志记录、数…

MySQL数据库复制

文章目录 MySQL数据库复制一、复制的原理二、复制的搭建1.编辑配置文件2.在主库上创建复制的用户3.获取主库的备份4.基于从库的恢复5.建立主从复制6.开启主从复制7.查看主从复制状态 MySQL数据库复制 MySQL作为非常流行的数据库,支撑它如此出彩的因素主要有两个&am…

Sourcetree——使用.gitignore忽略文件或者文件夹

一、为何需要文件忽略机制? 1.1 为什么要会略? 对于开发者而言,明智地选择忽略某些文件类型,能带来三大核心优势: 仓库纯净性:避免二进制文件、编译产物等污染代码库 安全防护:防止敏感信息&…

基于yolov8+streamlit实现目标检测系统带漂亮登录界面

【项目介绍】 基于YOLOv8和Streamlit实现的目标检测系统,结合了YOLOv8先进的目标检测能力与Streamlit快速构建交互式Web应用的优势,为用户提供了一个功能强大且操作简便的目标检测平台。该系统不仅具备高精度的目标检测功能,还拥有一个漂亮且…

分享vue好用的pdf 工具实测

vue3-pdf-app: 带大纲,带分页,带缩放,带全屏,带打印,带下载,带旋转 下载依赖: yarn add vue3-pdf-appornpm install vue3-pdf-app 配置类: 创建文件 pdfConfig.ts /…

基于微信小程序开发的宠物领养平台——代码解读

项目前端 一、项目的技术架构概况 一句话概括:该项目是基于微信小程序开发的宠物领养平台,采用原生小程序框架进行用户界面的构建,使用 wx.request 进行 API 请求,并通过 getApp() 和本地存储来管理全局状态和用户信息。 一&am…

最完美的WPF无边框设计!

常规的无边框方法设计 常规的WPF无边框设计方法都是通过AllowsTransparency="True"和WindowStyle=“None”,并且使用WindowChrome样式来实现,但是这样会有问题就是,窗体最大化的时候将底部任务栏给挡住了,另外最大化的时候不能拖动窗体。参考这个大佬的设计@ 若…

【区块链】btc

学习视频源链接: https://www.bilibili.com/video/BV1Vt411X7JF/ 本文是根据肖老师的视频进行的笔记记录 一、 cryptographic hash function 1.1. collision resistance抗碰撞性 : collision 指的是hash碰撞 抗碰撞性 (Collision Resistance) 是密码…

C语言【数据结构】:时间复杂度和空间复杂度.详解

引言 详细介绍什么是时间复杂度和空间复杂度。 前言:为什么要学习时间复杂度和空间复杂度 算法在编写成可执行程序后,运行时需要耗费时间资源和空间(内存)资源。因此衡量一个算法的好坏,一般是从时间和空间两个维度来衡量的,即时…

QT:文件读取

问题: 在文件读取,判断md5值时,遇到py文件读取转String后,再转byte,md5前后不一致问题。 解决方法: python文件读取要使用QTextStream,避免\t 、\r、\n的换行符跨平台问题(window…

32单片机——LED

LED原理图如图所示: 代码 DS0和DS1每过500ms一次交替闪烁,实现类似跑马灯的效果 GPIO输出配置步骤 (1)使能对应GPIO时钟 STM32在使用任何外设之前,我们都要先使能其时钟(下同)。本实验用到…

贪心算法和遗传算法优劣对比——c#

项目背景:某钢管厂的钢筋原材料为 55米,工作需要需切割 40 米(1段)、11 米(15 段)等 4 种规格 ,现用贪心算法和遗传算法两种算法进行计算: 第一局:{ 40, 1 }, { 11, 15…

【Java篇】一法不变,万象归一:方法封装与递归的思想之道

文章目录 Java 方法的使用:从基础到递归的全面解析一、方法的概念及使用1.1 什么是方法 (method)?1.2 方法定义1.3 方法调用的执行过程1.4 实参和形参的关系1.5 没有返回值的方法 二、方法重载2.1 为什么需要方法重载2.2 方法重载的概念2.2.4 C 和 Java 的比较&…

深入理解 HTML 中的<div>和元素:构建网页结构与样式的基石

一、引言 在 HTML 的世界里&#xff0c;<div>和元素虽看似普通&#xff0c;却扮演着极为关键的角色。它们就像网页搭建过程中的万能积木&#xff0c;能够将各种 HTML 元素巧妙地组合起来&#xff0c;无论是构建页面布局&#xff0c;还是对局部内容进行样式调整&#xff…

《大语言模型》学习笔记(一)

一、什么是大语言模型 大语言模型是指在海量无标注文本数据上进行预训练得到的大型预训练语言模型&#xff0c;例如GPT-3&#xff0c;PaLM和LLaMA。大语言模型&#xff08;Large Language Model&#xff0c;LLM&#xff09;是一种基于深度学习的自然语言处理模型&#xff0c;能…

电力行业中分布式能源管理(Distributed Energy Management System, DEMS)的实现

以下是电力行业中分布式能源管理(Distributed Energy Management System, DEMS)的实现方案,涵盖系统架构、关键技术、核心功能及实施路径,结合典型场景与代码示例: 一、系统架构设计 采用云-边-端三层架构,实现分布式能源的高效协同管理: 1. 终端层(感知层) 设备组…

实验5 逻辑回归

实验5 逻辑回归 【实验目的】掌握逻辑回归算法 【实验内容】处理样本&#xff0c;使用逻辑回归算法进行参数估计&#xff0c;并画出分类边界 【实验要求】写明实验步骤&#xff0c;必要时补充截图 1、参照“2.1梯度下降法实现线性逻辑回归.ipynb”和“2.2 sklearn实现线性逻辑…

思维训练让你更高、更强 |【逻辑思维能力】「刷题训练笔记」假设法模式逻辑训练题(1-5)

每日一刷 思维训练让你更高、更强&#xff01; 题目1 谁在说谎&#xff0c;谁拿走了零钱&#xff1f; 姐姐上街买菜回来后&#xff0c;就随手把手里的一些零钱放在了抽屉里&#xff0c;可是&#xff0c;等姐姐下午再去拿钱买菜的时候发现抽屉里的零钱没有了&#xff0c;于是&…

【愚公系列】《高效使用DeepSeek》004-DeepSeek的产品形态和功能详解

标题详情作者简介愚公搬代码头衔华为云特约编辑,华为云云享专家,华为开发者专家,华为产品云测专家,CSDN博客专家,CSDN商业化专家,阿里云专家博主,阿里云签约作者,腾讯云优秀博主,腾讯云内容共创官,掘金优秀博主,亚马逊技领云博主,51CTO博客专家等。近期荣誉2022年度…

用python代码将excel中的数据批量写入Json中的某个字段,生成新的Json文件

需求 需求&#xff1a; 1.将execl文件中的A列赋值给json中的TrackId&#xff0c;B列赋值给json中的OId 要求 execl的每一行&#xff0c;对应json中的每一个OId json 如下&#xff1a; {"List": [{"BatchNumber": "181-{{var}}",// "Bat…