怎么优雅地回收动态加载的纹理?

动态加载的资源我用了引用计数,使用的时候+1,销毁的时候-1,但是销毁时不好处理。于是想在生命周期中处理。

onDestroy 该方法为生命周期方法,父类未必会有实现。
_onPreDestroy 在对象被销毁之前调用。

但是断点发现这两个方法中Sprite上的SpriteFrame均为null,isValid为false,此时已经不能无法回收该纹理。

我项目目前的做法是做一个界面的基类,基类里有一个beforeDestroy()的方法,在界面管理器中即将销毁时调用beforeDestroy方法,这样可以确保在onDestroy之前执行,Sprite上的SpriteFrame还是有引用的,但是这样做有很大局限性:
1、必须手动在有加载动态资源的界面处理回收,麻烦且容易忘记
2、只局限在界面这个基类,部分没有继承基类的ui无法处理
3、面对复杂界面或嵌套的界面,如循环回收的列表等,不好处理

我目前被这问题搞的有点头秃,请问有啥好点的方式处理吗
项目用的ccc2.4.7

你要销毁的是资源,为什么会涉及sprite呢???你不应该记录的是spriteFrame吗?

spriteFrame是有记录的,记录了引用次数,涉及sprite是为了正确地做计数,只有在明确要销毁的时候才计数-1

  1. sprite(下面简写为sp)本身有脚本,则spriteFrame(下面简写为sf)记录在这个脚本里,这个脚本销毁,则sf的计数减1
  2. sp本身没有脚本,则记录在sp依赖的最上层的脚本上,该脚本销毁,则sf计数减一
    第二点的方法的问题,sf无法及时销毁,需要等父节点销毁才会回收。如果不能接受这个,就每个动态资源挂载的地方,都自动挂载脚本好了

你记录使用过的资源,在第一次使用时+1,然后在销毁时-1,不要处理已经存在的资源,

大致代码如下

@ccclass
export class UIRecorder extends Component {

	private loadedAssets = new Set<Asset>();
	
	public RecordLoadAsset(asset: Asset) {

		if (!this.loadedAssets.has(asset)) {
			this.loadedAssets.add(asset);
			asset.addRef();
		}
	}
	
	protected onDestroy(): void {
		for (const asset of this.loadedAssets) {
			asset?.decRef();
		}
		this.loadedAssets.clear();
		
	}
}

不知道你的具体使用方式,但从代码来讲,我个人认为是有问题的
image
如果资源要使用两次,其中有一次不需要销毁怎么办。当然如果你的这个脚本,单纯的绑定在某一个动态资源的节点上,也可以

感谢,当时脑子没转过弯来,想直接通过sp来回收sf,图省事了,正确的做法是分别记录

这个当然是挂在在具体节点上的