小狗做爰网站,ps制作网站,易企秀官网,学校ppt模板免费下载版本#xff1a; 3.4.0
语言#xff1a; TypeScript
环境#xff1a; Mac 回顾 前面有两篇博客说明了#xff1a;
cocosCreator 之 resources动态加载、预加载 讲述了静态引用资源#xff0c;动态加载和预加载相关cocosCreator 之 Bundle 讲述了AssetManager关于对内置…版本 3.4.0
语言 TypeScript
环境 Mac 回顾 前面有两篇博客说明了
cocosCreator 之 resources动态加载、预加载 讲述了静态引用资源动态加载和预加载相关cocosCreator 之 Bundle 讲述了AssetManager关于对内置Bundle和自定义Bundle的使用相关
简单的理解就是对cocosCreator内静态和动态引用资源的使用相关为了对动态资源更方便管理增加了AssetManager用于管理释放资源相关。 动态引用的资源相关接口均为异步操作 涉及到资源管理就会牵扯到资源的内存管理。
在cocosCreator中官方针对于不同的资源有着不同的内存管理方式。主要有
静态引用资源通过序列化数据进行自动管理释放动态引用资源为了避免错误释放而增加引用计数管理 以及AssetManager对资源进行的释放管理场景的自动释放管理
从本质上都是引用计数但为了有一个更好的理解故此通过本篇博客汇总出来。
理解可能有误欢迎您的指出。 引用计数 cocosCreator中的资源都被放在 assets目录下, 主要来源
从外部导入通过远程下载的资源
他们最后都会被包装使其继承于资源基类Asset。 #mermaid-svg-lQy1g9nKYf3h9WBZ {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-lQy1g9nKYf3h9WBZ .error-icon{fill:#552222;}#mermaid-svg-lQy1g9nKYf3h9WBZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-lQy1g9nKYf3h9WBZ .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-lQy1g9nKYf3h9WBZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-lQy1g9nKYf3h9WBZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-lQy1g9nKYf3h9WBZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-lQy1g9nKYf3h9WBZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-lQy1g9nKYf3h9WBZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-lQy1g9nKYf3h9WBZ .marker.cross{stroke:#333333;}#mermaid-svg-lQy1g9nKYf3h9WBZ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-lQy1g9nKYf3h9WBZ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-lQy1g9nKYf3h9WBZ .cluster-label text{fill:#333;}#mermaid-svg-lQy1g9nKYf3h9WBZ .cluster-label span{color:#333;}#mermaid-svg-lQy1g9nKYf3h9WBZ .label text,#mermaid-svg-lQy1g9nKYf3h9WBZ span{fill:#333;color:#333;}#mermaid-svg-lQy1g9nKYf3h9WBZ .node rect,#mermaid-svg-lQy1g9nKYf3h9WBZ .node circle,#mermaid-svg-lQy1g9nKYf3h9WBZ .node ellipse,#mermaid-svg-lQy1g9nKYf3h9WBZ .node polygon,#mermaid-svg-lQy1g9nKYf3h9WBZ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-lQy1g9nKYf3h9WBZ .node .label{text-align:center;}#mermaid-svg-lQy1g9nKYf3h9WBZ .node.clickable{cursor:pointer;}#mermaid-svg-lQy1g9nKYf3h9WBZ .arrowheadPath{fill:#333333;}#mermaid-svg-lQy1g9nKYf3h9WBZ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-lQy1g9nKYf3h9WBZ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-lQy1g9nKYf3h9WBZ .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-lQy1g9nKYf3h9WBZ .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-lQy1g9nKYf3h9WBZ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-lQy1g9nKYf3h9WBZ .cluster text{fill:#333;}#mermaid-svg-lQy1g9nKYf3h9WBZ .cluster span{color:#333;}#mermaid-svg-lQy1g9nKYf3h9WBZ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-lQy1g9nKYf3h9WBZ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 对象 事件处理 资源基类 cocos_core_assets_asset_Asset_base CCObject cocos_core_event_eventify_IEventified Asset 在cocosCreator中Asset的重要作用就是对资源进行引用计数。主要定义如下
// cc.d.ts
export class Asset extends __private.cocos_core_assets_asset_Asset_base {// 该资源对应的目标平台资源的 URL如果没有将返回一个空字符串get nativeUrl(): string;// 序列化对象serialize(): void;// 获取引用数量get refCount(): number;// 增加引用计数addRef(): Asset;// 减少资源的引用并尝试进行自动释放decRef(autoRelease?: boolean): Asset;
}// 主要实现 ../resources/3d/engine/cocos/core/assets/asset.ts
export class Asset extends Eventify(CCObject) {private _ref 0;// 引用计数数目public get refCount (): number {return this._ref;}// 引用计数1public addRef (): Asset {this._ref;return this;}// 引用计数-1并尝试进行自动释放public decRef (autoRelease true): Asset {if (this._ref 0) {this._ref--;}// 检测是否自动释放if (autoRelease) {legacyCC.assetManager._releaseManager.tryRelease(this);}return this;}
}针对于decRef下的自动释放接口 tryRelease, 我们看下大致的实现
// ../resources/3d/engine/cocos/core/asset-manager/release-manager.ts
class ReleaseManager {private _eventListener false;// 待释放资源数组private _toDelete new CacheAsset();// 尝试自动释放(释放对象是否强制释放默认为false)public tryRelease (asset: Asset, force false): void {if (!(asset instanceof Asset)) { return; }// 如果强制释放则释放资源if (force) {this._free(asset, force);return;}// 没有强制释放则将对象的uuid缓存到待释放资源对象中this._toDelete.add(asset._uuid, asset);// 检测对象是否注册事件监听器如果没注册则下一帧进行释放资检测if (!this._eventListener) {this._eventListener true;callInNextTick(this._freeAssets.bind(this));}}// 用于事件监听器的下一帧释放检测private _freeAssets () {this._eventListener false;this._toDelete.forEach((asset) {this._free(asset);});// 注意清空用于保证缓存的对象仅被遍历一次也就是生命周期仅有一帧this._toDelete.clear();}// 释放对象(对象是否强制释放)private _free (asset: Asset, force false) {const uuid asset._uuid;// 将释放对象从缓存中移除this._toDelete.remove(uuid);// 检测对象是否有效if (!isValid(asset, true)) { return; }if (!force) {// 检测引用计数和是否存在循环引用如果存在则returnif (asset.refCount 0) {if (checkCircularReference(asset) 0) { return; }}}// 从缓存中移除对象assets.remove(uuid);// 通过uuid获取资源的所有依赖项并进行遍历const depends dependUtil.getDeps(uuid);for (let i 0, l depends.length; i l; i) {// 对象有效则进行引用计数-1const dependAsset assets.get(depends[i]);if (dependAsset) {dependAsset.decRef(false);// no need to release dependencies recursively in editorif (!EDITOR) {this._free(dependAsset, false);}}}// ...}
}它的流程简介
如果不是强制释放对象则存储到临时数组中在下一帧遍历缓存中数组对象进行释放操作如果是强制释放对象则调用释放接口释放接口会将对象从临时数组中移除并检测对象是否有效、是否被引用如果对象可以被移除则获取依赖项并进行遍历进行引用计数-1引用计数为0则对对象进行释放。
这里有几点需要注意
针对于this._eventListener 是一个标记它主要用于保证对象需要在下一帧执行释放操作中的对象增加操作this._toDelete.clear()主要是为了保证对象的生命周期只有一帧。
针对于后者生命周期回调仅有一帧很像cocos2d-x中的内存管理处理
// application.cpp的while主循环中根据FPS每帧调用mainLoop
void Director::mainLoop() {if (! _invalid) {drawScene();// 清理当前释放池对象PoolManager::getInstance()-getCurrentPool()-clear();}
}void AutoreleasePool::clear() {// 通过使用vector.swap方法进行交换可以保证每帧仅对节点数据遍历一次std::vectorRef* releasings;releasings.swap(_managedObjectArray);// 遍历所有对象进行引用计数-1为0的销毁对象for (const auto obj : releasings) {obj-release();}
}关于cocos2d-x的内存机制可参考cocos2d-x 内存管理机制 cocosCreator中的资源很多都是相互依赖的他们的引用计数结构类似如下 当使用到某个资源时引用计数是 增加了一个资源的引用资源存在依赖性引用计数是 释放资源A引用计数是
引用计数为0的则进行释放操作。 动态引用
静态引用的资源会被编译器进行序列化后记录在序列化数据中引擎是可以统计引用关系的 所以不需要关注内存的释放相关。
但动态引用的资源使用灵活在需要的时候进行加载。
因为没有序列化引擎是无法统计引用关系的。导致引用计数为0就可能出现被误释放的问题。
因此需要借助addRef()和decRef()的接口进行手动管理:
const url img_bag/spriteFrame;
resources.load(url, SpriteFrame, (err, spriteFrame) {if (err) {return console.err(err.message);}let sprite this.node.getComponent(Sprite);sprite.spriteFrame spriteFrame;// 增加引用计数用于保证资源不被错误释放spriteFrame.addRef();this._spriteFrame spriteFrame;
});// 节点销毁时
protected onDestory() {if (this._spriteFrame) {this._spriteFrame.decRef();this._spriteFrame null;}
}注意 配对使用尤其针对于addRef如果频繁调用极大可能出现引用计数非0而内存浪费的问题。 AssetManager
官方提供的AssetManager模块用来负责加载、释放资源相关。在上面的示例中使用引用计数如果忘记依然存在内存泄漏的问题。
针对于内存管理AssetManager主要提供的接口有
export class AssetManager {// 已加载 bundle 的集合bundles: AssetManager.CacheAssetManager.Bundle;// 获取BundlegetBundle(name: string): AssetManager.Bundle | null;// 移除BundleremoveBundle(bundle: AssetManager.Bundle): void;// 已加载资源的集合assets: AssetManager.CacheAsset;// 释放资源以及其依赖资源, 不仅会从 assetManager 中删除资源的缓存引用还会清理它的资源内容releaseAsset(asset: Asset): void;// 释放所有没有用到的资源releaseUnusedAssets(): void;// 释放所有资源releaseAll(): void;
}注只要是Bundle都被AssetManager管理Bundle和Bundle内的资源移除是两码事 Bundle 在不使用后如果想移除需要优先释放 Bundle内的资源。
let bundle assetManager.getBundle(test_bundle);
if (!bundle) {return;
}
// 释放bundle内的所有资源
bundle.releaseAll();
// 移除Bundle
assetManager.removeBundle(bundle);关于AssetManager对 Asset 资源的释放相关看下引擎的主要实现
// ../resources/3d/engine/cocos/core/asset-manager/asset-manager.ts
export class AssetManager {public releaseAsset (asset: Asset): void {releaseManager.tryRelease(asset, true);}public releaseUnusedAssets () {assets.forEach((asset) {releaseManager.tryRelease(asset);});}public releaseAll () {assets.forEach((asset) {releaseManager.tryRelease(asset, true);});}
}releaseManager.tryRelease的具体实现看上面release-manager.ts的展示。 除了AssetManager 提供的资源释放以外 Bundle中也存在着一些释放接口它主要应用于对单一的资源释放。
let bundle assetManager.getBundle(test_bundle);
if (!bundle) {return;
}
// 释放bundle内的单个资源
bundle.release(image, SpriteFrame);
// 移除Bundle
assetManager.removeBundle(bundle);引擎中的主要实现代码
// ../resources/3d/engine/cocos/core/asset-manager/bundle.ts
export default class Bundle {// 释放包内指定路径的资源public release (path: string, type?: AssetType | null) {const asset this.get(path, type);if (asset) {releaseManager.tryRelease(asset, true);}}// 释放包内没有用到的资源public releaseUnusedAssets () {assets.forEach((asset) {const info this.getAssetInfo(asset._uuid);if (info !info.redirect) {releaseManager.tryRelease(asset);}});}// 释放包内所有的资源public releaseAll () {assets.forEach((asset) {const info this.getAssetInfo(asset._uuid);if (info !info.redirect) {releaseManager.tryRelease(asset, true);}});}
}releaseManager.tryRelease的具体实现看上面release-manager.ts的展示。 场景释放
针对于自动释放资源在场景的 属性检查器 中有个参数叫做 AutoReleaseAssets勾选。
场景在切换的时候也会进行自动释放该场景下的所有依赖资源。
主要的逻辑实现
director.loadScene或director.runScene时它们都会调用runSceneImmediate方法该方法会调用关于 release-manager.ts下的接口_autoRelease
// ../resources/3d/engine/cocos/core/asset-manager/release-manager.ts
// 场景的自动释放标记autoReleaseAssets
// 如果为true表示引用计数-1后进行自动释放即调用tryRelease接口
public _autoRelease (oldScene: Scene, newScene: Scene, persistNodes: Recordstring, Node) {// 检测是否有旧场景if (oldScene) {const childs dependUtil.getDeps(oldScene.uuid);for (let i 0, l childs.length; i l; i) {const asset assets.get(childs[i]);if (asset) {// 重要代码, 如果为true则调用tryRelease接口asset.decRef(TEST || oldScene.autoReleaseAssets);}}const dependencies dependUtil._depends.get(oldScene.uuid);if (dependencies dependencies.persistDeps) {const persistDeps dependencies.persistDeps;for (let i 0, l persistDeps.length; i l; i) {const asset assets.get(persistDeps[i]);if (asset) {// 重要代码, 如果为true则调用tryRelease接口asset.decRef(TEST || oldScene.autoReleaseAssets);}}}if (oldScene.uuid ! newScene.uuid) {dependUtil.remove(oldScene.uuid);}}// ...
}总结 cocosCreator的资源释放最后汇总下
资源相关的内存管理是引用计数通过Asset管理引用计数相关的逻辑操作在release-manager.ts中自动释放的主要代码思想是将释放的对象保存到临时数组中且该临时数组的生命周期仅有一帧场景相关建议勾选 AutoReleaseAssets 选项进行内存自动释放Bundle相关建议合理使用release、 releaseUnusedAssets、 releaseAll的接口AssetManager相关 释放Bundle的时候注意资源释放接口的调用(同Bundle名称一样)
最后祝大家学习生活愉快!