内存管理:
C/C++内存管理机制 引用计数机制 自动释放池 纹理缓存在对Cocos2d的内存管理机制进行认识之前,我们先回忆一下之前C/C++里面的内存管理机制new / delete,malloc / free,帮助我们更好的认识Cocos2d的内存管理。
C/C++内存管理机制
在C中,我们如果要为对象,变量分配内存,我们会使用malloc,对应释放用free;在C++中,我们会new一个对象来分配内存,用delete来做释放。
我们在普通项目中新建两个文件GameObject.h以及GameObject.cpp,如图:
GameObject.h:
#ifndef __GAMEOBJECT_H__#define __GAMEOBJECT_H__class GameObject{public: int count; GameObject(); ~GameObject();private:};#endif // !__GAMEOBJECT_H__GameObject.cpp:
#include "GameObject.h"GameObject::GameObject() { count = 1;}GameObject::~GameObject(){}main.cpp:
int main(){ // c++中内存管理的方法,这里会调用构造函数和析构函数 GameObject *gameobject = new GameObject(); gameobject->count = 0; delete gameobject; GameObject *gameobject2 = new GameObject[4]; gameobject2->count = 0; delete[] gameobject2; // c中内存管理的方法,需要强制类型转换,c 中不会调用构造函数,因为c没有类 GameObject *gameobject3 = (GameObject*)malloc(sizeof(GameObject)); gameobject3->count = 0; free(gameobject3);}
引用计数机制
引用计数的关键作用在于,当我们创建一个对象后,该对象被其他对象引用,为防止在释放的时候出现错误,我们使用引用计数表明被引用的次数,因而需要所有引用对象及对象本身被释放,此对象才能算是被完全释放。修改上面的HelloWorld项目。
GameObject.h:
<pre name="code" >#ifndef __GAMEOBJECT_H__#define __GAMEOBJECT_H__class GameObject{public: int age; int count; GameObject(); ~GameObject(); voID freeobject();private:};#endif // !__GAMEOBJECT_H__GameObject.cpp:
#include "GameObject.h"GameObject::GameObject(){ count = 1;}GameObject::~GameObject(){ freeobject();}voID GameObject::freeobject(){ --count; if (count <= 0) { delete this; }}main.cpp:
#include <iostream>using namespace std;int main(){ // 引用计数机制 GameObject *gameobject = new GameObject(); // 创建一次对象,引用计数count加 1 GameObject *gameobject2 = gameobject; // 这里引用计数的作用在于防止gameobject被释放后,gameobject2的内存为空,导致使用时出错 gameobject->count++; cout << count << gameobject->count << endl; // 调用函数释放对象,释放时引用计数减 1,直到引用计数为 0时释放对象 gameobject->freeobject(); cout << count << gameobject->count << endl; // 这里能调用age说明对象还没有被释放,只是引用计数减 1 gameobject2->age = 2; gameobject2->freeobject(); cout << count << gameobject->count << endl; // gameobject->count = 0; // 这里引用计数已经为0,对象被释放,因此调用时会出错}
输出结果:
count:2
count:1
count:-17891602
Cocos2d的引用计数函数:
sprite->retain() // 引用计数加1sprite->release() // 释放对象,引用计数减1sprite->getReferenceCount() // 获取引用计数节点数引用计数函数:
virtual voID addChild(CCNode *child); virtual voID addChild(CCNode *child,int zOrder); virtual voID addChild(CCNode *child,int zOrder,int tag); virtual voID removeChild(CCNode *child,bool cleanup); voID removeChildByTag(int tag,bool cleanup); virtual voID removeAllChildrenWithCleanup(bool cleanup);
新建一个Cocos2d项目“HelloWorld”,在HelloWorldScene.cpp中的bool HelloWorld::init()函数中添加以下代码:
CCSprite *sprite = new CCSprite(); log("spritecount:%d",sprite->getReferenceCount());<span > </span>// 获取引用计数 CCSprite *sprite2 = sprite; sprite2->retain();<span > </span>// 引用计数加1 *** 作 log("spritecount:%d",sprite->getReferenceCount()); // 这里被添加到图层,引用计数也会加1 this->addChild(sprite); log("spritecount:%d",sprite->getReferenceCount()); this->removeChild(sprite); log("spritecount:%d",sprite->getReferenceCount()); sprite2->release();<span > </span>// 释放对象,引用计数减 1 log("spritecount:%d",sprite->getReferenceCount()); sprite->release(); log("spritecount:%d",sprite->getReferenceCount());
运行结果:
spritecount:1
spritecount:2
spritecount:3
spritecount:2
spritecount:1
spritecount:-17891602
自动释放池(CCautoreleasePool)
CCautoreleasePool不能被开发者自己创建。Cocos2d-x会为我们每一个游戏创建一个自动释放池实例对象,游戏开发者不能新建自动释放池,仅仅需要专注于release/retain cocos2d::CCObject的对象。
静态构造函数包含autorelease()函数,调用这样的函数不需要手动释放,自动释放池会帮我们做释放,这样的静态函数有:
CCAsdf::createWithXxxx(…)object->init(…)
代码实例:
<pre name="code" >CCSprite *sprite = CCSprite::create("HelloWorld.png");this->addChild(sprite);等同于
CCSprite *sprite = new CCSprite(); sprite->initWithfile("HelloWorld.png"); sprite->autorelease();<span > </span>// 对象会在结束时被自动释放 this->addChild(sprite);
纹理缓存(TextureCache)
CCTextureCache继承自TextureCache,纹理(纹理可看做像素)缓存是将纹理缓存起来方便之后的绘制工作。每一个缓存的图像的大小,颜色和区域范围都是可以被修改的。这些信息都是存储在内存中的,不用在每一次绘制的时候都发送给GPU。下次可直接利用缓存,避免重新绘制,减少cpu和GPU内存的消耗。
下面为Cocos2d中的源代码,会把添加的图片加入到纹理缓存,这些在Cocos2d底层实现,我们可以直接调用:
<pre name="code" >bool Sprite::initWithfile(const std::string& filename){ CCASSERT(filename.size()>0,"InvalID filename for sprite"); // 由导演获取纹理缓存,缓存图片,下次需要图片时可直接从texture获取 Texture2D *texture = Director::getInstance()->getTextureCache()->addImage(filename); if (texture) { Rect rect = Rect::ZERO; rect.size = texture->getContentSize(); return initWithTexture(texture,rect); } // don't release here. // when load texture Failed,it's better to get a "transparent" sprite then a crashed program // this->release(); return false;}
V3.0新特性(官方发表内容):
Sprite 和 SpriteBatchNode @H_404_217@ v2.2 2.2版本中推荐的优化游戏方式是将SpriteBatchNode
对象设置为Sprite
对象的父节点。 虽然使用SpriteBatchNode
对象仍然是一个非常好的优化游戏的方式,但是它仍然有一定的限制: Sprite
对象的孩子只能是Sprite
(否则,Cocos2d-x 会触发断言) 当Sprite
的父节点是SpriteBactchNode
时,不能添加ParticleSystem
作为Sprite
的子节点。 这将导致当SpriteBatchNode
时,不能使用ParallaxNode
所有的Sprite
对象必须共享相同的纹理ID (否则,Cocos2d-x 会触发断言) Sprite
对象使用SpriteBatchNode
的混合函数和着色器。 @H_404_217@ 虽然 v3.0 仍然支持SpriteBatchNode
(与之前的版本拥有相同的特效和限制),但是我们不鼓励使用它。相反,我们推荐直接使用Sprite
,不需要再它作为子节点添加到SpriteBatchNode
中。 @H_404_217@ 但是,为了能让 v3.0 有更好的表现,你必须要确保你的Sprite
对象满足以下条件: 贡献相同的纹理ID(把它们放在一个Spritesheet中,就像使用SpriteBatchNode
一样) 确保它们使用相同的着色器和混合函数(就像使用SpriteBatchNode
一样) @H_404_217@ 如果这么做,Sprites
将会像使用SpriteBatchNode
一样的快...(在旧设备上大概慢了10%,在新设备上基本上察觉不出) @H_404_217@ v2.2 和 v3.0 最大的区别在于: Sprite对象可以有不同的纹理ID。 Sprite
对象可以有不同种类的Node
作为子节点,包括ParticleSystem
。 Sprite
对象可以有不同的混合函数和不同的着色器。 @H_404_217@ 但是如果你这么做,渲染器
可能无法对它所有的子节点进行批处理(性能较低)。但是游戏仍然可以正常运行,不会触发任何断言。 @H_404_217@ 总结: 保持将所有的精灵放在一张大的 Spritesheet 中。 使用相同的混合函数(使用默认) 使用相同的着色器(使用默认) 不要将精灵添加到SpriteBatchNode
@H_404_217@ 只有当你需要一些额外的性能上提升(虽然很小),SpriteBatchNode
才会是你最后的选择(你需要对它的限制条件很熟悉)。 提示和技巧(摘自官方文档)
一帧一帧载入游戏资源 载入纹理时按照从大到小的顺序 避免高峰内存使用 使用载入屏幕预载入游戏资源 需要时释放空闲资源 收到内存警告后释放缓存资源. 使用纹理打包器优化纹理大小、格式、颜色深度等 使用JPG格式要谨慎! 请使用RGB4444颜色深度16位纹理 请使用NPOT纹理,不要使用POT纹理 避免载入超大纹理 推荐1024*1024 NPOT pvr.ccz纹理集,而不要采用RAW PNG纹理 总结以上是内存溢出为你收集整理的Cocos2d-x学习笔记(四)—— 内存管理全部内容,希望文章能够帮你解决Cocos2d-x学习笔记(四)—— 内存管理所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)