分享一个极简的资源释放解决方案
本文实现基于Cocos Creator 3D v1.2.0
Creator 3D v1.2.0 demo:https://forum.cocos.org/uploads/short-url/i3qOaa5QFBwVXuLSba7Ru2WQJkt.rar Cocos v2.2.2 demo ResCleaner-cocos-v222.zip|attachment (446.5 KB)
场景的资源自动释放是有问题的,官方已经修复了。补丁地址:<https://github.com/cocos-creator/engine/pull/7619 >
特点
- 使用简单,无管理负担。
- 项目接入成本低,只需要处理对象池部分即可。
- 性能消耗跟场景上的节点数成正比。
原理
释放逻辑如下:
- 遍历场景上所有节点,搜集每个节点上所有组件引用的资源。
- 收集所有正在加载中的资源(loader.loadRes、loader.loadResArray)
- 遍历资源缓存(loader._cache)。如果当前资源既没有被场景节点引用又不属于加载中则释放。
代码讲解
核心代码如下:
另外,代码中有部分函数是从引擎源码(auto-release-utils.ts)中拷贝出来的,因为引擎没有对外暴露这些函数。
难点在于加载中资源的剔除。大致逻辑如下:首先获取所有加载中的加载队列。然后对加载队列依赖的资源进行递归资源引用查找。
项目接入
- 引入脚本文件ResCleaner.ts到项目当中
- 检查项目所有使用对象池的地方。如果完全没有使用到对象池直接跳到step4。否则执行step3.
- 由于引擎内置的NodePool在节点回收的时候会从场景上移除,导致节点无法被资源清理函数遍历到。所以需要自己开发一个对象池。新实现的对象池需要利用node.active = false来避免使用node.removeFromParent()。样例如下:
直接使用一个数组存储相同预制体 - 调用资源清理函数 ResCleaner.clean()。调用时机建议在场景切换之后,也可以在收到
内存不足告警之后调用。
释放效果演示
测试代码如下:
执行流程:
在start里面开始加载预制体
加载完成后实例化成节点并添加到场景上
调用资源清理函数
将节点移出场景并销毁
调用资源清理函数
将成员变量spframe对资源的引用置空
再次调用资源清理函数
MainCity场景初始状态
spframe初始资源引用
prefabA内容
打印的日志如下:
代码和日志结合起来看
图解一下整个过程:
初始
预制体加载完成成
实例化成节点并添加到场景上
调用资源清理后
节点销毁后
调用资源清理后
调用 this.spframe = null
调用资源清理后
注意事项
- 节点如果要复用请使用node.active = false替代node.removeFromParent()。
- 以下几种情况资源引用不会被统计到
- 使用非组件脚本引用资源。如果类不是继承自Component,就无法挂在节点上,也就无法被统计到。
- 在组件脚本中间接引用资源。出于性能考虑不支持。
- 使用组件的静态成员变量引用资源。感觉用的不多所以暂不支持。