讨论下2.4.0的引用计数

WHAT?刚告诉我就是不加,你这又加了???:joy:

我是以场景作为当前运行环境,释放的时候校验手动加载持有和场景持有(动态资源+静态资源+自定义组件拖挂资源+所有资源的依赖资源) ;未被任何形式持有的 才能真实的释放掉

假释放接口提供了,释放单资源(uuid,cc.Asset, path),释放目录,释放节点
提供两个真实释放接口 releaseAllUnused 和 releaseManualUnused,内部进行了环境标记后的安全释放

测试结果是销毁精灵后,它所使用的spf和tex2d的引用计数没变还是1, 这个按说在节点销毁时,应该可以自动把引用的资源释放掉吧?

目前是不会的,场景中直接引用的资源不会与某个节点绑定,只会与场景绑定。所以你在场景切换的时候,如果勾选了自动释放的话,应该可以看到这个资源被释放掉。

在经过手动对spf调用decRef后,它引用的tex2d的引用计数却没变化,这个不应该自动减1嘛?

是应该减一的,不过这个操作不是立即执行的,会延迟到本帧的最后一个阶段执行,所以你执行之后打印出来的结果还是老的结果。你可以延迟个100ms来看,texture 应该被释放了

了解,这个真实释放接口可能比较适合中小型项目,大项目上万个节点都是有可能的,这么遍历一次,不太吃得消啊。而且跟组件复杂度有关,组件复杂度越高,消耗越高

我没测试过,所以你们可以系统测试一下
而且真实释放时,不是随意调用的,这和加载较大较多资源导致的掉帧属于同一个问题(在2.4以前,2.4在资源管理上确实是优秀的)

的确感觉很简单不错,是不是还得考虑如果用到节点池,不在场景的节点时的额外处理?

是的,节点池,就需要手动标记和移除标记。但是我的项目资源是按模块+目录管理的,都是无脑释放目录,由于没有做全局节点池,所以直接无视了节点池的情况:grinning:

主要一般节点池都是加载预制,这时就被手动加载持有标记上了,一般没有直接从场景上剥离出一个节点做节点池

大佬回复的真快:joy:,的确如你所说。不过不仅仅是场景引用的资源不会和节点绑定,自己手动创建的节点也不会和资源绑定,要释放的话都需要自己手动decRef,比如销毁精灵时如果想立即释放资源就需要对引用的资源decRef, 总算明白了!

:joy:这些情况也太复杂了呀,想起来都让我头秃…我们只提供了简单的增减操作,具体使用得看具体使用场景…

是的,因为目前的引用计数的统计,是通过比较简单的,在加载资源的过程中统计依赖资源统计出来的。所以依赖资源只能和资源绑定,比如 cc.SceneAsset 和 cc.Prefab,不能和 node 挂钩,如果要和 node 挂钩的话,就是上面的小猪所说的,得遍历整个场景,我们目前没有采取这种方案

1赞

这个坎有点难迈了

大佬我这还有个疑问,就是同一个精灵在切换贴图时,新的贴图引用计数不对,不知道我这样写是不是有问题,旧纹理的确释放掉了,但新纹理的计数不对:

start () {
        let spf: cc.SpriteFrame = this.testSp.spriteFrame;
        let tex2d: cc.Texture2D = spf.getTexture();
        cc.log('spfRef: ', spf.refCount);  // 1
        cc.log('texRef:', tex2d.refCount);  // 1

        cc.resources.load('textures/test2', cc.SpriteFrame, (err, frame: cc.SpriteFrame)=>{
            if (!err) {
                this.testSp.spriteFrame.decRef(); // 释放原来的纹理
                frame.addRef(); // 引用新纹理
                this.testSp.spriteFrame = frame;

                cc.log('a:', frame.refCount); //  1
                cc.log('b:', frame.getTexture().refCount);  // 1
            }

            
            this.scheduleOnce(t=>{
                cc.log('old spfRef: ', spf.refCount);  // 0
                cc.log('old texRef:', tex2d.refCount);  // 0

                cc.log('new spf:', this.testSp.spriteFrame.refCount);  // 1
                cc.log('new tex2d: ', this.testSp.spriteFrame.getTexture().refCount);  // 0 ?? 为何不是1?释放旧纹理时把新纹理释放了?
            }, 0);
        });
    }

能提供一个demo么,我看下

你好,demo如下:
changeSpfDemo.zip (1.1 MB)

我看了下,引用计数是对的哈,因为启用了动态合图,所以你用下面这种方式返回的图片其实不是原来那个texture,是一张引擎内部生成的大图,所以自然计数为0了。

this.testSp.spriteFrame.getTexture()

你可以用 uuid 来查找原来那张图,就可以看到引用计数是正常的1,或者不启用动态合图,也能返回的是1.

我感觉这些可以提供一些接口,由开发者设置与某个节点挂钩,然后内部有一个使用标记记录,node节点destroy后会自己尝试释放被这个node节点标记过使用的资源,但没有其他节点有使用过这个资源而且没有被引用那就可以直接release这个资源,不知道这样子ok不ok

基于2.4的设想,是否可以在节点或组件使用某一个资源时 进行计数+1,销毁或替换的资源时进行计数-1。这种应该完全能够实现吧,通过重写指定属性的set方法。举个例子,比如精灵组件设置spriteframe,设置时进行计数+1,替换销毁后计数-1.

嗯,我们看下