12-05 348 views
垃圾回收器的压缩操作是托管的堆与非托管的堆的区别所在.—C#高级编程第10版
以后写文章以引语开篇, 我感觉这样风格更酷.
代码看过的不一定是你的, 但写过的一定会融化到你血液里 , 最近抽了”点”时间重写了下这里, 有了更深的理解, 不废话上图:

对于上图上层加载说明:
- 纯lua/C#类对象, 可以使用弱表/数组缓存, 避免每次new
- 对于需要实例化的GameObject/UIComponent(如播放音乐需要多个AudioSource)可以用循环队列管理实例化出来的资源, 并进行实例化时间控制, 实现负载均衡
- 当资源refCount为0时放入LRU进行末尾淘汰, 每次取用重置池对象的destroyTime, 池属性可以根据高中低配置动态改变
- 特殊的对于Atlas, 只有其中每一个Sprite都不再被引用, 总引用才算清零, 回收其Texture
- 短音频同步加载池缓存, 长音频采用流式加载避免过多占用内存
- lua池和C#资源池似乎看上去有重叠, 但作用不尽相同, Lua对象池一直持有着资源, 避免了lua从新和资源结合的过程(如transform.Find操作), 而C#端保持引用, 避免从新加载AB和从AB加载资源的过程
//对于unity2019流加载音乐, 有下面格式: private IEnumerator WWWLoad(string path, Action<Object> callback) { string fullPath = string.Empty; if (DebugSelector.AllUseResources) { fullPath = "file://" + PathUtil.ResPath + path; } else { fullPath = PathUtil.GetStreamingWWWPath(path); } //注意Editor下不支持mp3流式加载 using (UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip(fullPath, AudioType.OGGVORBIS)) { var handler = www.downloadHandler as DownloadHandlerAudioClip; handler.streamAudio = true; handler.compressed = false; yield return www.SendWebRequest(); if (!www.isDone) { yield return null; } if (www.isNetworkError) { Debugger.LogError("[UnBundleLoadManager] Can not Load Music, {0}, {1} ", www.error, www.url); callback(null); } else { AudioClip clip = DownloadHandlerAudioClip.GetContent(www); callback(clip); } } }

对于上图底层加载的说明:
- 对于异步加载,无论A资源是从AssetBundle.Load还是Resources.Load总有下面的逻辑:
- (可选)检查加载资源的配置, 无配置返回空
- 检查所有缓存的资源, 如果已经有此资源缓存, 缓存引用计数+1, 返回资源
- 检查此加载任务是否存在, 若存在添加完成回调到此任务, 每个加载完成回调中资源引用计数+1, 返回并缓存资源
- 创建新加载任务, 添加完成回调, 加载完成回调中资源引用计数+1, 返回并缓存资源
- 特殊对于加载出空资源, 不增加引用计数, 返回空
- 对于从AB中加载A资源, 引用的a, b, B的加载都有上述过程
- 对于加载A资源, a, b, B任何一个加载失败, A都返回空, 不增加引用计数
- A被多次加载, 只在第一次增加依赖资源的引用计数, 并在引用为0时, 减少依赖资源的引用
- 如果B资源是不需要显式加载的 , 我们可以忽略B资源存在, 而只需要维护b的引用即可
关于AB标标记,如果资源被公用, 请标记ABName, 否则资源会被复制分别打入引用他的资源所在AB中, 科普文(英文版)
如果需要明确的AssetBundle.Unload(True), 我们需要自己维护A, B, a, b的引用关系,manifests文件表示鸭梨山大恕不奉陪
如果一个资源所在AB没有被公用, 那么我们可以AssetBundle.Unload(False)提前卸掉AB而不用担心资源被加载多份
对于AB, 异步加载和同步加载问题:
- 异步加载A资源还好说, 但是如果同步加载A资源:
- 如果a, B, b刚好全都被提前的加载缓存了非常棒一切顺利;
- 如果a, B, b加载任务都不存在, 全创建为同步加载任务也没啥问题;
- 但若B未被提前加载出来并缓存, 而是因为被某个C资源引用(C资源被加载导致B需要被加载)而处于异步加载任务中, 则麻烦了, 我们只能碰个运气赌一下B资源这一帧能否被加载出来, 如果可以, 皆大欢喜同步返回被加载好的A资源, 否则只能返回空了,加载失败
可见, 对于上层的加载接口, 最好大部分资源类型规定为异步加载, 同步加载的资源最好限制为可控的几种(如shader, mat, Text)对于底层的加载接口,AB的同步加载 (AssetBundle.LoadFromFile声称只会加载头部, 足够优秀) 尽量使用同步; 而从AB中加载资源也尽量大部分使用同步, 少部分大资源使用异步加载方式, 并保证外部不会尝试同步加载这些资源(或者小心维护好)
版权属于: CrazyStone Entertainment
转载时必须以链接形式注明原始出处及本声明。