两个继承Component类的内存泄漏问题

这篇博客探讨了两个由于继承Component类导致的内存泄漏问题:Ping类和ManagementObjectCollection类。虽然Ping类实现了IDisposable接口,但它并未正确覆盖Component的Dispose方法,导致资源未被释放。ManagementObjectCollection的情况类似。文章引用了外部资源并提供了两种解决方法,建议使用方法一来避免内存泄漏。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Ping类中的内存泄漏

在这里插入图片描述
Ping继承了Component类,Component在析构时会调用

在这里插入图片描述
但Component在析构调用中传递的是false,即只释放非托管资源,不释放托管资源,所以不手动释放托管资源就会造成内存泄漏
后来增加了pinger.Dispose();问题依旧没有得到解决,经查阅资料了解到Ping.Dispose()并非实现了IDisposable.Dispose()方法,而是直接继承了Component.Dispose(),换言之,这个方法根本没有对Ping对象的非托管资源进行释放。反而Ping类显示实现了IDisposable接口,必须将其强转为IDisposable才能正确调用真正的Dispose()方法。再看Component类,Component.Dispose()方法是一个非虚方法,而Component.Dispose(bool)是一个虚方法,Component.Dispose()方法只是对Component.Dispose(bool)的一个简单调用。按照微软的设计思想,Ping类应该重写Component.Dispose(bool)方法,实现资源释放,而不应显式实现IDisposable接口。因此,这应是一个bug。微软已声明将修复它,但从.NET 2.0开始,直到目前.NET 4.0 Beta 1经实验依然没有解决。
国外探讨此问题的文章:http://blog.mbcharbonneau.com/2006/11/14/using-the-ping-class-in-net-20-without-memory-leaks/
Ping源码:https://referencesource.microsoft.com/#System/net/System/Net/NetworkInformation/ping.cs,1eb489d34930cef7

解决方法,强烈推荐采用方法一:
方法一:
在这里插入图片描述
方法二:
在这里插入图片描述

ManagementObjectCollection类中的内存泄漏

在这里插入图片描述
原理同Ping,ManagementObjectSearcher也继承自Component,解决方法同上。
在这里插入图片描述

<think>我们有两个自定义事件管理:`EventMgr` 和 `EventManager`。它们都是用于在 Cocos Creator 中管理事件的,但实现方式有所不同。下面我将详细分析它们的不同之处。 ### 1. EventMgr 分析 - 这个是一个 Component组件),使用了 `@ccclass` 装饰器,意味着它可以被挂载到场景中的节点上。 - 它使用了静态方法,因此不需要实例化即可使用。 - 在 `onLoad` 生命周期中,它将当前节点设置为静态变量 `_node`,并且通过 `director.addPersistRootNode` 将该节点设为常驻节点,这样在切换场景时不会被销毁。 - 事件监听和派发都是通过这个静态的 `_node` 节点进行的。它提供了 `on`, `once`, `emit`, `off`, `offAll`, `has` 等方法,这些方法都是对节点事件系统的封装。 ### 2. EventManager 分析 - 这个是一个普通的 TypeScript ,没有使用 `@ccclass` 装饰器,因此不是组件,不能挂载到节点上。 - 它实现了单例模式,通过静态属性 `_ins` 保存唯一实例,并通过静态方法 `ins` 获取该实例。 - 它使用 Cocos Creator 的 `EventTarget` 作为事件系统的基础。`EventTarget` 是 Cocos Creator 中用于事件管理的似于浏览器中的 `EventTarget`。 - 需要手动调用 `init` 方法初始化(创建 `EventTarget` 实例)。 ### 主要区别 1. **实现方式**: - `EventMgr` 基于节点(Node)的事件系统,而 `EventManager` 基于 `EventTarget`。 - `EventMgr` 本身是一个组件,必须挂载到节点上,并且依赖于场景中的节点(虽然通过常驻节点使其持久化)。 - `EventManager` 是一个纯逻辑,不依赖于场景节点,但需要初始化(调用 `init` 方法)。 2. **单例模式**: - `EventMgr` 通过静态方法和静态变量实现,但它的核心是一个节点(`_node`)。由于节点在场景中,所以它实际上依赖于场景(虽然通过常驻节点避免了销毁)。 - `EventManager` 显式地实现了单例模式,通过 `ins` 属性获取唯一实例。 3. **事件系统**: - `EventMgr` 使用节点的事件系统(`node.on`, `node.emit` 等),这些方法在 Cocos Creator 中用于节点之间的事件通信。 - `EventManager` 使用 `EventTarget`,这是一个更轻量级的事件系统,不依赖于节点,适合全局事件管理。 4. **使用方式**: - `EventMgr` 可以直接通过静态方法使用,例如 `EventMgr.on(...)`。 - `EventManager` 需要先获取实例(`EventManager.ins`),然后通过实例的 `eventTarget` 属性来使用事件方法(例如 `EventManager.ins.eventTarget.on(...)`),或者可以在 `EventManager` 中再封装一层似的方法。 5. **初始化**: - `EventMgr` 的初始化在 `onLoad` 中完成,当节点加载时自动执行(需要将该组件挂载到场景中的一个节点上)。 - `EventManager` 需要显式调用 `init` 方法(例如在游戏启动时调用一次)。 ### 代码示例对比 #### 使用 EventMgr ```typescript // 在某个组件中监听事件 EventMgr.on('event_name', this.callback, this); // 派发事件 EventMgr.emit('event_name', arg1, arg2); ``` #### 使用 EventManager ```typescript // 首先确保初始化(通常在游戏启动时) EventManager.ins.init(); // 监听事件 EventManager.ins.eventTarget.on('event_name', this.callback, this); // 派发事件 EventManager.ins.eventTarget.emit('event_name', arg1, arg2); ``` ### 改进建议 1. **EventManager 的封装**:为了更方便使用,可以在 `EventManager` 中封装似 `EventMgr` 的静态方法,这样就不需要每次都写 `ins.eventTarget`。 例如: ```typescript export class EventManager { // ... 其他代码同上 public static on(type: string, callback: Function, target?: any) { this.ins.eventTarget.on(type, callback, target); } // 似地封装 once, emit, off 等方法 } ``` 2. **事件参数传递**:`EventMgr` 的 `emit` 方法最多支持5个参数(arg0到arg4),而 `EventTarget` 的 `emit` 方法可以传递任意多个参数(使用剩余参数)。所以 `EventManager` 在事件参数传递上更灵活。 3. **内存管理**:两者都提供了取消注册事件的方法,但需要注意在组件销毁时取消注册,避免内存泄漏。 ### 总结 - 如果你希望事件管理器与节点系统紧密结合,并且希望利用场景中的节点(比如需要节点的事件冒泡等特性),可以选择 `EventMgr` 的方式。 - 如果你希望一个轻量级、不依赖于场景节点的全局事件管理器,那么 `EventManager` 的方式更合适。 在实际项目中,推荐使用 `EventManager` 的方式,因为它不依赖于场景节点,更加独立和灵活。同时,我们可以对其进行改进,封装成静态方法,使其使用起来更加方便。 ### 改进后的 EventManager 示例代码 ```typescript import { EventTarget } from 'cc'; export class EventManager { private static _ins: EventManager; public eventTarget: EventTarget; static get ins() { if (this._ins == null) { this._ins = new EventManager(); this._ins.eventTarget = new EventTarget(); } return this._ins; } // 封装静态方法,方便调用 public static on(type: string, callback: Function, target?: any) { this.ins.eventTarget.on(type, callback, target); } public static once(type: string, callback: Function, target?: any) { this.ins.eventTarget.once(type, callback, target); } public static emit(type: string, ...args: any[]) { this.ins.eventTarget.emit(type, ...args); } public static off(type: string, callback?: Function, target?: any) { this.ins.eventTarget.off(type, callback, target); } // 可以添加其他方法,如移除某个对象的所有事件监听 public static offAll(target: any) { this.ins.eventTarget.targetOff(target); } } ``` 使用方式: ```typescript // 不需要显式初始化,因为获取实例时会自动初始化 // 监听事件 EventManager.on('event_name', this.callback, this); // 派发事件(可以传递任意多个参数) EventManager.emit('event_name', arg1, arg2, arg3); // 取消监听 EventManager.off('event_name', this.callback, this); ``` 这样,`EventManager` 的使用方式就和 `EventMgr` 一样方便了,同时它不依赖于节点,更加灵活。 ### 相关问题 §§相关问题§§: 1. Cocos Creator 中的事件系统(Node 事件和 EventTarget)有什么区别? 2. 在 Cocos Creator 中,如何避免事件监听导致的内存泄漏? 3. 单例模式在游戏开发中有哪些常见应用场景? 4. 如何实现一个全局的事件管理器,支持跨场景通信?</think># Cocos Creator 自定义事件管理对比分析 我将分析您提供的两种自定义事件管理(EventMgr 和 EventManager)的不同之处。 ## 核心区别分析 ### 1. 实现方式与继承关系 ```typescript // EventMgr:基于 Component 组件系统 @ccclass('Manager/EventMgr') export class EventMgr extends Component { // ... } // EventManager:纯逻辑(无继承) export class EventManager { // ... } ``` **区别**: - EventMgr 继承Component,可以挂载到场景节点上 - EventManager 是纯逻辑,不依赖场景节点 - EventMgr 需要被添加到场景中才能工作 - EventManager 可以直接在代码中使用 ### 2. 单例实现方式 ```typescript // EventMgr:静态方法实现 public static on(type: string, callback: Function) { ... } // EventManager:显式单例模式 private static _ins: EventManager; static get ins() { if (this._ins == null) { this._ins = new EventManager(); } return this._ins; } ``` **区别**: - EventMgr 通过静态方法实现功能,无需实例化 - EventManager 使用经典单例模式,通过 `EventManager.ins` 访问实例 - EventMgr 本质上也是单例(通过常驻节点实现) ### 3. 事件系统实现 ```typescript // EventMgr:使用节点事件系统 private static _node: Node; public static on(type: string, callback: Function) { this._node.on(type, callback); } // EventManager:使用 EventTarget public eventTarget: EventTarget; public init() { this.eventTarget = new EventTarget(); } ``` **区别**: | 特性 | EventMgr (Node) | EventManager (EventTarget) | |------|----------------|----------------------------| | 事件冒泡 | 支持 | 不支持 | | 事件捕获 | 支持 | 不支持 | | 性能 | 较重(与场景节点关联) | 较轻(独立事件系统) | | 参数传递 | 最多5个参数 | 无参数限制 | | 使用场景 | 节点相关事件 | 全局逻辑事件 | ### 4. 初始化方式 ```typescript // EventMgr:自动初始化(onLoad生命周期) protected onLoad(): void { EventMgr._node = this.node; director.addPersistRootNode(this.node); } // EventManager:手动初始化 public init() { if (this.eventTarget == null) { this.eventTarget = new EventTarget(); } } ``` **区别**: - EventMgr 自动初始化(需要挂载到场景节点) - EventManager 需要显式调用 `init()` 方法 - EventMgr 自动设为常驻节点(切换场景不销毁) - EventManager 不依赖场景生命周期 ### 5. 内存管理 ```typescript // EventMgr:提供显式的解绑方法 public static offAll(target: unknown): void { this._node.targetOff(target) } // EventManager:依赖开发者手动管理 // (需要自行实现似功能) ``` **区别**: - EventMgr 提供 `offAll()` 方法,方便批量解绑 - EventManager 需要开发者自行实现似功能 - 两者都需要注意避免内存泄漏 ## 使用场景建议 ### 适用 EventMgr 的场景: 1. 需要与场景节点交互的事件 2. 需要事件冒泡/捕获机制 3. 需要常驻的场景事件管理器 4. 项目已基于节点事件系统构建 ### 适用 EventManager 的场景: 1. 纯逻辑层的事件通信 2. 跨场景的全局事件管理 3. 高性能要求的场景(避免节点开销) 4. 需要传递多个参数的事件 ## 改进的 EventManager 实现 结合两者优点,改进后的 EventManager: ```typescript import { EventTarget } from 'cc'; export class EventManager { private static _ins: EventManager; private _eventTarget: EventTarget; // 私有构造函数确保单例 private constructor() { this._eventTarget = new EventTarget(); } static get ins(): EventManager { if (!this._ins) { this._ins = new EventManager(); } return this._ins; } // 监听事件 on(type: string, callback: Function, target?: any, once?: boolean) { if (once) { this._eventTarget.once(type, callback, target); } else { this._eventTarget.on(type, callback, target); } } // 发射事件(支持任意数量参数) emit(type: string, ...args: any[]) { this._eventTarget.emit(type, ...args); } // 取消监听 off(type: string, callback?: Function, target?: any) { this._eventTarget.off(type, callback, target); } // 取消对象的所有监听 offAll(target: any) { this._eventTarget.targetOff(target); } // 检查是否有监听 has(type: string, callback?: Function, target?: any): boolean { return this._eventTarget.hasEventListener(type, callback, target); } } ``` ### 改进点: 1. 内置初始化逻辑(无需手动调用init) 2. 支持任意数量参数的事件传递 3. 提供完整的型声明 4. 统一事件接口(on/once使用同一方法) 5. 保留批量解绑功能 ### 使用示例: ```typescript // 监听事件 EventManager.ins.on('PLAYER_DEAD', this.onPlayerDead, this); // 发射事件(任意数量参数) EventManager.ins.emit('ITEM_COLLECTED', itemType, quantity, playerId); // 取消监听 EventManager.ins.off('PLAYER_DEAD', this.onPlayerDead, this); // 取消对象所有监听 EventManager.ins.offAll(this); ``` ## 总结 1. **架构差异**: - EventMgr 基于 Component 和 Node 事件系统 - EventManager 基于独立的 EventTarget 2. **生命周期**: - EventMgr 依赖场景生命周期 - EventManager 独立于场景 3. **事件能力**: - EventMgr 支持完整节点事件特性(冒泡/捕获) - EventManager 更轻量,适合全局通信 4. **参数传递**: - EventMgr 最多支持5个参数 - EventManager 支持任意数量参数 5. **使用便捷性**: - EventMgr 静态方法直接调用 - EventManager 需要访问单例实例 根据项目需求选择: - 需要节点交互 → EventMgr - 需要全局通信 → EventManager - 需要最佳实践 → 改进版EventManager
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值