Swift EventBus讲解

一、EventBus 是什么?

一句话:EventBus = 全局事件消息中转站 专门用来:跨页面、跨类、跨模块 解耦通信

最早是安卓经典三方库(GreenRobot EventBus)iOS 没有原生 EventBus,但是:苹果原生 NotificationCenter 就是苹果自带的低配版 EventBus

解决什么痛点?

你平时通信有多恶心:

  1. A 页面 想通知 C 页面刷新
  2. 工具类想通知 ViewModel 弹窗
  3. 底层网络层报错,通知所有页面提示

常规写法:代理、闭包嵌套、单例属性传值、强引用耦合→ 代码乱、耦合严重、难维护

EventBus 终极方案:

我只管「发一条消息」谁需要监听,谁就「订阅这条消息」完全不用互相引用、不用传参数、零耦合


二、EventBus 核心三大角色(死记)

  1. 事件 Event自定义一个模型 / 标记,当做「消息暗号」比如:登录成功事件、退出登录事件、刷新列表事件

  2. 发布者 Publisher 任意地方(VC/VM/ 网络层 / 工具类)一行代码:发送事件

  3. 订阅者 Subscriber需要接收消息的地方,提前注册监听收到事件自动回调执行业务代码

中间由 Bus 总线 统一转发、管理所有订阅。


三、完整工作流程

  1. 订阅者:注册监听「某某事件」
  2. 发布者:在任意位置,发送「某某事件」
  3. 总线收到消息,遍历所有监听该事件的对象
  4. 自动分发回调,订阅者执行业务
  5. 页面销毁 → 手动解除订阅(防内存泄漏、野指针

四、EventBus 独有特色(安卓原版)

1. 自动线程切换

发消息在子线程,接收可以自动切主线程不用自己写 DispatchQueue.main

2. 粘性事件 Sticky(重点)

普通事件:先订阅,后发消息才能收到 粘性事件:先发消息,后面再订阅,也能收到上一条缓存消息

场景:启动页先发「用户登录状态」,首页后加载、后订阅,也能拿到之前的事件

3. 注解 + 反射(安卓)

通过注解标记接收方法,不用手动注册,iOS 不用这套


五、iOS 等价对照

技术 定位 级别
EventBus (安卓) 全局事件总线 强、功能全
NotificationCenter 苹果官方原生通知 弱、字符串硬编码
手写 自定义 EventBus 泛型强类型总线 企业常用
Combine PassthroughSubject 局部数据流通信 页面内 / 模块内

核心区别(超级重要)

  1. NotificationCenter
  • 基于 String 名字,弱类型,容易写错字符串崩溃
  • 全局广播,到处都能发
  1. Combine
  • 局部、强类型
  • 适合单个 ViewModel 和 View 绑定,不适合全局跨模块
  1. EventBus(自定义)
  • 强类型、泛型约束
  • 全局跨模块解耦,比通知好用、安全

六、手写一个「极简 iOS 版 EventBus」

代码极少,你一眼看懂底层原理,和写 Combine 思路一模一样。

Swift 复制代码
import Foundation

// 1. 定义事件协议(所有事件都遵守它)
protocol EventType {}

// 2. 自定义具体事件
struct LoginSuccessEvent: EventType
struct LogoutEvent: EventType

// 3. 全局 EventBus 总线
final class EventBus {
    // 单例,全局唯一
    static let shared = EventBus()
    private init() {}
    
    // 存储:[事件类型: 订阅回调数组]
    private var eventDict: [Any.Type: [Any]] = [:]
    
    // 订阅
    func on<T: EventType>(_ type: T.Type, handler: @escaping (T) -> Void) {
        eventDict[T.self, default: []].append(handler)
    }
    
    // 发送事件
    func post<T: EventType>(_ event: T) {
        guard let handlers = eventDict[T.self] else { return }
        handlers.forEach { block in
            if let action = block as? (T) -> Void {
                action(event)
            }
        }
    }
    
    // 销毁订阅(防泄漏)
    func clear<T: EventType>(_ type: T.Type) {
        eventDict.removeValue(forKey: T.self)
    }
}

使用

Swift 复制代码
// 🔹 页面A:订阅监听登录事件
EventBus.shared.on(LoginSuccessEvent.self) { _ in
    print("收到登录成功,刷新页面")
}

// 🔹 任意地方:比如网络层/登录页 发事件
EventBus.shared.post(LoginSuccessEvent())

✅ 完全解耦:发消息的人,完全不知道谁在监听收消息的人,完全不知道谁发的


七、EventBus / Notification / Combine 怎么选?

1. 用 EventBus(自定义)

  • 跨页面、跨组件、跨底层工具类
  • 需要强类型,不想用字符串通知
  • 全局状态广播:登录、退出、版本更新、全局弹窗

2. 用 NotificationCenter

  • 简单临时通知、小项目、快速开发
  • 缺点:字符串硬编码、无类型校验

3. 用 Combine(@Published / Subject)

  • 页面内部、ViewModel 和 View 通信
  • 数据流连续变化(输入框、列表刷新、状态变更)
  • MVVM 绑定首选

八、EventBus 致命坑(必记)

  1. 必须手动销毁订阅不然对象释放不了,严重内存泄漏
  2. 禁止滥用全局广播到处乱发事件,项目后期完全无法调试
  3. 多线程发送要加锁,防止数组崩溃
  4. 循环订阅导致死循环

九、结合之前的知识闭环

  1. 属性包装器 + Combine → 页面内 数据绑定、UI 刷新
  2. MVVM → 分层解耦
  3. EventBus → 全局跨模块 事件解耦
  4. Notification → 系统原生低配全局通信

十、一句话终极总结

  1. EventBus = 强类型版 全局通知中心
  2. 核心:发布 - 订阅模式,彻底解耦跨页面通信
  3. 局部用 Combine,全局用 EventBus / 通知
  4. 安卓标配 EventBus,iOS 项目一般自己手写轻量 EventBus 替代垃圾字符串通知
相关推荐
aq55356001 小时前
GitSubmodule深度避坑指南
java·开发语言·php
止语Lab2 小时前
Go 的测试框架不想让你 TDD
开发语言·golang·tdd
yaoxin5211232 小时前
391. Java 文件操作基础 - 方法链式调用
java·开发语言·python
t***5442 小时前
如何在 Dev-C++ 中配置 Clang 编译器集
开发语言·c++
小碗羊肉2 小时前
【从零开始学Java | 第四十一篇】深入多线程
java·开发语言
覆东流2 小时前
第7天:Python小项目
开发语言·后端·python
四眼蒙面侠2 小时前
Open Agent SDK (Swift):用原生 Swift 构建 AI Agent 应用
swift·agentsdk
qq_254617772 小时前
attribute((constructor)) 在C/C++中的应用
开发语言·c++
xyq20242 小时前
HTML5 Input 类型详解
开发语言