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

news2025/3/13 2:26:17

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

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

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

相关文章

Python从入门到精通1:FastAPI

引言 在现代 Web 开发中,API 是前后端分离架构的核心。FastAPI 凭借其高性能、简洁的语法和自动文档生成功能,成为 Python 开发者的首选框架。本文将从零开始,详细讲解 FastAPI 的核心概念、安装配置、路由设计、请求处理以及实际应用案例&a…

Leetcode做题记录----2

1、两数之和 思路: 1、不能使用相同元素,可以想到哈希表,,C#中可以通过字典建立当前值和下标的关系 2、显然,依次判断数组中的每个数即可 3、定义other target - num[ i ] 这个other就是我们用于在字典中进行寻找…

批量合并 Word 文档,支持合并成一个 Word,也支持按文件夹合并

我们经常会碰到需要将多个 Word 文档批量合并成一个 Word 文档的场景,比如需要合并后打印、合并后方便整理存档等等。如果是人工的操作,会非常的麻烦。因此我们通常会借助一些批量处理脚本或者寻找批量处理的工具来帮我们实现批量合并 Word 文档的操作。…

项目实操分享:一个基于 Flask 的音乐生成系统,能够根据用户指定的参数自动生成 MIDI 音乐并转换为音频文件

在线体验音乐创作:AI Music Creator - AI Music Creator 体验者账号密码admin/admin123 系统架构 1.1 核心组件 MusicGenerator 类 负责音乐生成的核心逻辑 包含 MIDI 生成和音频转换功能 管理音乐参数和音轨生成 FluidSynth 集成 用于 MIDI 到音频的转换 …

神经网络为什么要用 ReLU 增加非线性?

在神经网络中使用 ReLU(Rectified Linear Unit) 作为激活函数的主要目的是引入非线性,这是神经网络能够学习复杂模式和解决非线性问题的关键。 1. 为什么需要非线性? 1.1 线性模型的局限性 如果神经网络只使用线性激活函数&…

动态规划详解(二):从暴力递归到动态规划的完整优化之路

目录 一、什么是动态规划?—— 从人类直觉到算法思维 二、暴力递归:最直观的问题分解方式 1. 示例:斐波那契数列 2. 递归树分析(以n5为例) 3. 问题暴露 三、第一次优化:记忆化搜索(Memoiza…

ubuntu下在pycharm中配置已有的虚拟环境

作者使用的ubuntu系统位于PC机上的虚拟机。系统版本为: 在配置pycharm解释器之前你需要先创建虚拟环境以及安装pycharm。 作者创建的虚拟环境位于/home/topeet/miniconda3/envs/airproject/,如下图所示: 作者安装的pycharm版本为2023社区…

爬虫中一些有用的用法

文本和标签在一个级别下 如果文本和a标签在一个级别下 比如: # 获取a标签后的第一个文本节点text_node a.xpath(following-sibling::text()[1])[0].strip() 将xpath的html代码转换成字符串 etree.tostring(root, pretty_printTrue, encoding"utf-8")…

DeepIn Wps 字体缺失问题

系统缺失字体 Symbol 、Wingdings 、Wingdings2、Wingdings3、MT—extra 字体问题 问了下DeepSeek 在应用商店安装或者在windows 里面找 装了一个GB-18030 还是不行 在windows里面复制了缺失的字体 将字体复制到DeepIn 的字体目录(Ubuntu 应该也是这个目录&am…

【webrtc debug tools】 rtc_event_log_to_text

一、rtc_event_log 简介 在学习分析webrtc的过程中,发现其内部提供了一个实时数据捕获接口RtcEventLog。通过该接口可以实时捕获进出webrtc的RTP报文头数据、音视频配置参数、webrtc的探测数据等。其内容实现可参考RtcEventLogImpl类的定义。其文件所在路径 loggin…

数字IC后端项目典型问题(2025.03.10数字后端项目问题记录)

小编发现今天广大学员发过来的问题都比较好,立即一顿输出分享给大家(每天都有好多种类的数字后端问题)。后续可能会经常通过这种方式来做分享。其实很多问题都是实际后端项目中经常遇到的典型问题。希望通过这种方式的分享能够帮助到更多需要…

Redis 持久化详解:RDB 与 AOF 的机制、配置与最佳实践

目录 引言 1. Redis 持久化概述 1.1 为什么需要持久化? 1.2 Redis 持久化的两种方式 2. RDB 持久化 2.1 RDB 的工作原理 RDB 的触发条件 2.2 RDB 的配置 2.3 RDB 的优缺点 优点 缺点 3. AOF 持久化 3.1 AOF 的工作原理 AOF 的触发条件 3.2 AOF 的配置…

说一下spring的事务隔离级别?

大家好,我是锋哥。今天分享关于【说一下spring的事务隔离级别?】面试题。希望对大家有帮助; 说一下spring的事务隔离级别? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Spring的事务隔离级别是指在数据库事务管理中…

Java 大视界 -- 基于 Java 的大数据实时数据处理框架性能评测与选型建议(121)

💖亲爱的朋友们,热烈欢迎来到 青云交的博客!能与诸位在此相逢,我倍感荣幸。在这飞速更迭的时代,我们都渴望一方心灵净土,而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识,也…

NAT NAPT

NAT NAT(Network Address Translation,网络地址转换) 主要用于在不同网络(如私有网络和公共互联网)之间进行 IP 地址转换,解决IP 地址短缺问题,并提供一定的安全性。 IPv4 地址是 32 位&#xf…

harmonyOS(鸿蒙)— 网络权限(解决app网络资源无法加载,图片无法显示)

harmonyOS系列 harmonyOS(网络权限) 一、问题梳理二、代码及图示 一、问题梳理 在鸿蒙app的开发里会有联网业务无法加载,图片无法显示的情况,多半是系统的网络权限没有申请,所以无法使用需要网络加载的资源&#xff0…

Python毕业设计选题:基于django+vue的疫情数据可视化分析系统

开发语言:Python框架:djangoPython版本:python3.7.7数据库:mysql 5.7数据库工具:Navicat11开发软件:PyCharm 系统展示 管理员登录 管理员功能界面 用户管理 员工管理 疫情信息管理 检测预约管理 检测结果…

清华同方国产电脑能改windows吗_清华同方国产系统改win7教程

清华同方国产电脑能改windows吗?清华同方国产电脑如果采用的是兆芯kx-6000系列或kx-7000系列以及海光c86 3250 3350 X86架构处理器可以安装windows。在安装win7时bios中要关闭“安全启动”和开启legacy传统模式支持,如果是NVME接口的固态硬盘&#xff0c…

【redis】string应用场景:缓存功能和计数功能

文章目录 缓存功能实现思路存在的问题伪代码实现 记数功能实现思路统计伪代码实现 缓存功能 实现思路 整体的思路: 应用服务器访问数据的时候,先查询 Redis 如果 Redis 上数据存在了,就直接从 Redis 读取数据交给应用服务器,不继…

vue el-select 省市区三级联动 vue + element-ui使用第三方插件实现省市区三级联动

vue el-select 省市区三级联动 vue使用第三方插件实现省市区三级联动 网上找了好多教程,都是使用el-cascader级联选择器的省市区选择器,但是几乎没有三个单独的el-select的进行关联的三级省市联动组件效果 第一步:先安装省市区element-ui的插件 npm install element-china-a…