【资源回收】当界面内加载一个预制体,没加载完毕界面就被关闭了

当界面内加载一个预制体A,没加载完毕界面就被关闭了,关闭的界面设置parent=null;
那预制体A,就一直是失活状态,当界面被销毁了,预制体A就调不到onDestroy,这样的话,我如何减少预制体A的引用呢

节点已销毁

1、简单粗暴,发现节点被销毁,直接释放

xxBundle.load(url, cc.Prefab, (err, APrefab) => {
    if(!isValid(node)) {
        // 通过这种方式直接释放
        APrefab.addRef();
        APrefab.decRef(true);
    }
})

2、发现节点被销毁,延迟释放

xxBundle.load(url, cc.Prefab, (err, APrefab) => {
    if(!isValid(node)) {
        // 通过这种方式直接释放
        APrefab.addRef();
        Your_Auto_Release_Ref_Pool.push(APrefab)
    }
})

// Your_Auto_Release_Ref_Pool 根据策略,自动释放
Your_Auto_Release_Ref_Pool.prototype.Release = function() {
        let toReleases: Asset[] = GET_TO_RELEASE_ASSETS(); 
        toReleases.forEach(a => a.decRef(true))
}

节点没销毁

xxBundle.load(url, cc.Prefab, (err, APrefab) => {
    if(isValid(node)) {
        // 引用住,节点销毁时释放。
        APrefab.addRef();
        (node.getComponent(AutoDestroy) || node.addComponent(AutoDestroy)).addDestroyFunc(()=> {
            APrefab.decRef(true);
        })
    }
})

谁便举例了几个,可以参考下大致的思路。
最重要的是,使用起来一定要方便。因此要封装成底层的 API,或者组件,上层使用的时候,不关心内部实现,而且可调整缓存和释放策略。

2赞

这个是加载完毕时候的发现销毁时候的回调,那如果加载完毕的时候已经加入了没有激活的父节点,当父节点销毁的时候,就调不到了

那问题来了,那个节点既然未激活,你的加载为什么要开始呢?

或者你的释放,为什么又要绑定到一个未激活的节点上?你的设计是否有问题

1赞

比如界面里面,需要 加载一个按钮,按钮是挂在了界面内的一个子节点里面,打开界面立马加载了,加载时间比如需要3秒,我在3秒内关掉了界面。那这样动态加载的预制体都需要判断所加入的节点是否是激活状态(activeInHierarchy)?会不会有点麻烦呢

封装好就不麻烦。
另外一个,不是判断激活状态。
最后,界面加载,最好是放在顶层那个界面负责加载。给你看个我最后封装成这样的方式:

界面依赖一个资源集合的方式:

资源集合中依赖界面预制体、图片、音频、图集,还能自定义解析:

这样资源依赖写顶层。释放也由顶层来负责。

非常动态的内容,比如列表滚动过程加载释放,通过特定组件去封装加载,写好一次,后续就不麻烦。

1赞

您说的顶层是 在需要加载的界面写一个 加载 和 释放 的静态函数吗?然后手动调用释放和销毁?

是 ui.open(界面)的时候,ui 管理模块会创建资源收集器,去收集依赖资源,然后加载,然后根据界面释放情况,去决定资源的释放是立即释放,还是绑定到界面的 onDestory 去。。。

我的这个静态 R 函数,就是收集过程中,由 ui 管理器负责递归调用的。

1赞

啊,了解了你的意思了。那这样子处理的话,不是提前加载页面所有所需的资源了吗?比如界面中有3个页面,进入界面的时候只需要显示第一个页面,那就不需要加载后2个页面了。不过您这样子确实避免了上述我所说的问题

那就不添加动态加载的那 2 个资源
那 2 个资源,单独创建加载器去加载。

这种是提供了一种界面打开就加载所有依赖的能力。怎么用,是由人决定的。。。

1赞

image
您的做法和我以前Laya的产品很像。嘻嘻。现在新项目想着不人为收集,我再斟酌一下,应该可以避免一下。谢谢您给的提醒

不人为收集是不可能的,界面要加载哪些东西,是业务逻辑决定的。
你工程里有哪些资源,倒是可以自动解析生成。

举个例子,哪个预制体在哪个bundle,路径是哪个,完全可以解析出来。

1赞

是的。目前是这样子用的。使用的时候仅需要一个名字就可以加载并且使用了。只是目前您比我多了这个,释放起来就更方便image

这是我们目前直接在代码里面使用的
image

你这样用起来会比较难受,尤其是界面中的子节点
因为要 instantiate 的时候,没有马上出来,出现异步操作,一定要判定父节点是否已销毁。其实是很难受的。

1赞

是的,这里在回收时候时候有点难受。但是业务用起来其实舒服。已经太多用的地方了,看看能不能挽回

嗯,如果没有节点提前销毁的话,你这用起来其实还是比我这个更舒服。

不过因为每个界面只需要收集自己的依赖资源即可,而依赖资源又依赖哪些资源,那是依赖资源自己的事情,收集流程会完成依赖资源的递归收集。因此,收集代码和使用代码写在同一个文件里的两个位置,整体还是能接受的。

1赞

是的,分出来之后怎么弄都好说。到时候实在不行,我就扩展一下引擎,目前不好改写法了

1赞