Files
XCEngine/docs/api/XCEngine/Core/Event/Event.md

119 lines
3.6 KiB
Markdown
Raw Normal View History

2026-03-26 16:45:24 +08:00
# Event
**命名空间**: `XCEngine::Core`
2026-03-27 19:18:53 +08:00
**类型**: `class template`
2026-03-26 16:45:24 +08:00
**头文件**: `XCEngine/Core/Event.h`
2026-03-27 19:18:53 +08:00
**描述**: 一个轻量模板事件广播器,支持订阅、延迟取消订阅和参数化回调调用。
2026-03-26 16:45:24 +08:00
2026-03-27 19:18:53 +08:00
## 角色概述
2026-03-26 16:45:24 +08:00
2026-03-27 19:18:53 +08:00
`Event<Args...>` 是当前代码库里最常用的基础广播机制之一。它的定位很明确:
2026-03-26 16:45:24 +08:00
2026-03-27 19:18:53 +08:00
- 用模板参数表达回调签名
-`Subscribe()` 返回监听器 ID
-`Invoke()` 把参数广播给所有当前监听器
2026-03-26 16:45:24 +08:00
2026-03-27 19:18:53 +08:00
和一些商业引擎里的事件系统相比,它更偏轻量 C++ 容器封装而不是完整的消息总线、typed dispatcher 或 editor event graph。
2026-03-26 16:45:24 +08:00
2026-03-27 19:18:53 +08:00
## 当前真实使用
2026-03-26 16:45:24 +08:00
2026-03-27 19:18:53 +08:00
当前已能在多个核心模块里看到它的直接使用,例如:
- `Scene::OnGameObjectCreated()`
- `Scene::OnGameObjectDestroyed()`
- `SceneManager` 的场景切换事件
- `InputManager` 的键鼠输入事件
这说明它不是“理论上的基础设施”,而是已经进入主代码路径的常用类型。
## 当前实现行为
### 1. `Subscribe()` 会分配递增 ID
- 监听器以 `(uint64_t id, Callback)` 形式保存在 `m_listeners`
- ID 由 `m_nextId` 递增生成
- `tests/core/test_core.cpp` 已验证不同订阅返回的 ID 不同
### 2. `Unsubscribe()` 是延迟取消订阅
`Unsubscribe(id)` 当前不会立刻从 `m_listeners` 删除,而是先把 ID 放进 `m_pendingUnsubscribes`
真正删除发生在两种时机:
- 显式调用 [ProcessUnsubscribes](ProcessUnsubscribes.md)
- 下一次 [Invoke](Invoke.md) 开始前
这意味着 API 语义更接近“标记取消订阅”,而不是“立刻从容器抹掉”。
### 3. `Invoke()` 会在锁外执行回调
`Invoke()` 的当前流程是:
1. 加锁
2. 先处理 `m_pendingUnsubscribes`
3. 复制 `m_listeners` 到本地 `listenersCopy`
4. 释放锁
5. 逐个执行回调
这套做法的关键收益是:
- 回调执行期间不会长期占着互斥锁
- 回调内部即使再次订阅/取消订阅,也不直接破坏当前这次遍历
代价是会有一次监听器数组复制,且它不是为极端高频无分配场景优化的实现。
## 线程语义
### 已有保护
- `Subscribe()``Unsubscribe()``ProcessUnsubscribes()``Invoke()``Clear()` 都会访问 `m_mutex`
### 不能误解的地方
- [begin](begin.md) / [end](end.md) 直接返回内部 `std::vector` 迭代器,没有加锁包装
- 如果你跨线程持有这些迭代器,同时别的线程订阅/取消订阅,就没有安全保证
所以可以说:它对常规订阅和调用路径做了基础互斥保护,但并不是“所有访问方式都线程安全”的容器。
## 当前实现限制
- `begin()` / `end()` 暴露的是内部容器迭代器,不适合并发或长时间持有。
- 回调列表在 `Invoke()` 时会拷贝一份,适合易用性,但不适合把它当成极致性能事件系统。
- 没有 listener priority、事件消费中止或过滤器机制。
- 没有 scoped subscription / RAII subscription token。
## 测试覆盖
`tests/core/test_core.cpp` 已覆盖:
- 订阅与回调调用
- 带参数事件
- 取消订阅
- 清空监听器
- 多监听器广播
- 返回 ID 唯一性
## 相关方法
- [Subscribe](Subscribe.md)
- [Unsubscribe](Unsubscribe.md)
- [ProcessUnsubscribes](ProcessUnsubscribes.md)
- [Invoke](Invoke.md)
- [Clear](Clear.md)
- [begin](begin.md)
- [end](end.md)
## 相关指南
- [Core Foundations: Ownership, Events, And Layers](../../../_guides/Core/Core-Foundations-Ownership-Events-And-Layers.md)
2026-03-26 16:45:24 +08:00
## 相关文档
2026-03-27 19:18:53 +08:00
- [当前模块](../Core.md)
- [Scene](../../Scene/Scene/Scene.md)
- [InputManager](../../Input/InputManager/InputManager.md)
- [API 总索引](../../../main.md)