cocos2d 内存管理机制详解
2012-12-21 17:54
363 查看
搜集网上一些关于cocos2d内存管理机制的Blog,贴出来方便大家阅读
cocos2d-x内存管理机制解析(一):http://blog.csdn.net/a7833756/article/details/7628328
Cocos2d-x内存管理(二):http://blog.csdn.net/a7833756/article/details/7632199
cocos2d-x学习笔记18:内存管理01:概述:http://4137613.blog.51cto.com/4127613/784134
cocos2d-x初探学习笔记(13)--内存回收机制:http://www.2cto.com/kf/201202/118721.html
cocos2d-x内存管理机制解析(一):http://blog.csdn.net/a7833756/article/details/7628328
Cocos2d-x内存管理(二):http://blog.csdn.net/a7833756/article/details/7632199
cocos2d-x学习笔记18:内存管理01:概述:http://4137613.blog.51cto.com/4127613/784134
cocos2d-x初探学习笔记(13)--内存回收机制:http://www.2cto.com/kf/201202/118721.html
cocos2d-x 内存释放的机制和使用要注意的地方:http://blog.csdn.net/a7833756/article/details/7450307
cocos2d-x内存管理机制解析(一):
前言:c++内存机制,采用new关键字实例化的对象,必须在不使用的时候手动delete掉,否则new的时候开辟的内存就不能被回收,造成内存泄露。
我们来举个例子说明一下:
运行 结果,vld工具提示了存在内存泄露:
当加上delete操作后:
下面步入正题:
1、cocos2d-x 内存管理的方式,cocos2d-x采用引用计数的方式进行内存管理,当一个对象的引用计数为0的时候,就会被引擎自动delete掉。
所有cocos2d-x里面的类都继承ccobject类(应该是吧、),下面看ccobject类源码:
这里 m_uReference 就是引用计数,在对象构造的时候,m_uReference置为1
然后每次对对象进行retain操作,reference+1
每次对对象进行release操作,reference-1,如果reference在这次release之后变为0,那么delete掉它
2、以上是内存管理的基本原则,下面来讲引擎中是怎么对对象进行自动管理(autorelease)的。
我们以一个CCNode的生命历程为例,来讲一下自动管理的整个过程:
首先创建一个CCNode:
CCNode对象被new出来之后,立刻执行autorelease操作,我们来进行跟踪:
可以看到,首先将m_bManaged置为true,表示处于自动管理状态,然后加入自动管理池,继续跟踪:
getCurReleasePool() 返回一个 CCAutoreleasePool 对象指针,也就是一个自动释放池,那么我们先去看看这个自动释放池里面有什么:
首先就是一个ccobject数组,m_pManagedObjectArray , 这个数组放的就是接受自动释放的对象,也就是说,进行autorelease的对象,最终被放到它里面去了
那我们注意一下这里,m_pManagedObjectArray 是一个 CCMutableArray 对象,它的addobject()方法除了把一个对象放到这个array里面去,还做了什么呢,我们来看看源码:
大家应该看到了,进行了一次retain()操作,使得对象的引用+1,那么对象在被add到这个array里之后,引用应该为2(不考虑其他地方进行的retain),所以在此之后立刻进行了一次release(),使得这次add造成的引用取消,这样一来,对象从创建开始引用为1,到现在被放进自动释放池中后,引用依然为1,同时,被管理状态为true。
那么我再进行深入的分析一下
getCurReleasePool()->addObject(pObject);
里面的getCurReleasePool()方法,注意这个方法是 CCPoolManager 的方法:
那么我看到 CCPoolManager 类,顾名思义,我们也想得到它是对自动释放池进行管理的类,这是个全局变量,在main()函数执行之前由系统自动调用其默认构造方法进行实例化。下面进入这个类去看看里面有些什么:
我们发现了,里面有一个 CCAutoreleasePool 对象 m_pCurReleasePool, 看它名字, 可以理解为 当前自动释放池 ,然后有一个内存池的数组 m_pReleasePoolStack ,里面放的就是多个内存释放池了(我好像就看到一个进去了)。那么我们连上刚才的思路,进去看看push()函数,做了什么事情:
如代码所示,push()操作new了一个自动释放池对象,并且将它赋值给 当前自动释放池(m_pCurReleasePool),然后将这个new的自动释放池对象放到CCPoolManager 里面的 释放池数组中。注意过程中对其引用计数的控制,自动释放池本身也是继承ccobject的,也有自己的引用计数,受相同的内存管理方式。
那么到这里,一个对象的autorelease()过程就完成了。
那么我来做一个简单的总结:
首先 new 一个对象, 然后执行其autorelease()方法,接下来是得到CCPoolManager 对象(这是一个全局的对象),用它的getCurReleasePool获取 当前自动释放池对象,并将这个new的对象放入 当前自动释放池对象 里面的m_pManagedObjectArray数组中,修改其被管理状态 m_bManaged 为 true。执行完这个完整的操作之后,这个新new出来的对象的引用次数为1,被管理状态为true,并且被放在一个管理对象的数组中。
先理解自动释放池的添加过程,并且搞清楚对象被添加前后之间的变化,然后下一篇,来讲被自动管理的对象是如何被引擎自动删除的。
Cocos2d-x内存管理(二):
前一篇我们讲到cocos2d-x里的内存管理机制,以及引擎中的自动内存管理机制。一个被自动管理的对象从new出来之后到被放到autoreleasepool那么接下来,对象是如何被引擎自动delete掉的呢?首先我们要知道,cocos2d-x的引擎线程是单线程的,它不停的调用voidCCDisplayLinkDirector::mainLoop(void)来绘制当前的Scene ,同时对一些自动释放的对象进行管理。我们先到一个cocos2d-x项目的main()函数里面:
这里调用了一个run()方法,我们跟踪进去:
Run方法有个while(1)循环,不断的调用mainLoop(void)方法,来完成界面渲染和对象释放,我们进mainLoop():
于是我们看到了引擎对自动管理的对象进行释放的操作。它调用的,是CCPoolManager的pop()方法,我们去看看这个pop()方法:
如代码里面所示,m_pReleasePoolStack就是之前提到的当前内存池,也就是内存池管理者里面那个内存池堆栈的栈顶的那个内存池,对其进行clear()操作,记住,在clear()之前,被放置在自动管理池内的对象的引用次数都是为1的(依然只考虑对象被new出来之后马上autorelease()操作并且在其他地方不进行retain()),那么进行clear()操作时:
CCAutoreleasePool会做这样两件事情,首先会把池中所有对象的被管理状态置为false,表示对象已经不再处于自动管理状态,然后清除管理池中所有的对象引用:
在这一过程中,会调用每个对象的release()方法,这样,我们算一下,之前对象的引用次数为1,那么在这里进行一次release之后,引用为0,就会执行delete操作,这样一来,这个被管理的对象就被成功释放掉了。
下面我们来写个小demo验证一下这个过程:
New一个对象:
s的引用次数为1,被管理状态为false:
对其进行autorelease()
s的引用依然为1,被管理状态为true:
然后进行一次retain()操作:
引用变为2
接下来刷下一帧,刷完后我们再看o对象,发现其被管理状态变为false,引用变成1了,这说明如果我们之前没有手动执行过retain(),这个对象已经被引擎给回收掉了。
这样一来,验证了引擎的自动回收机制,我们可以在retain()后面release()一次,来看看对象o被delete后的状态:
发现其内部的数据都变成随机数值了,也就是对象已经被清除了。
最后我来做个简单的总结:
Cocos2d-x中,采用引用计数的方式进行内存管理,谁需要引用这个对象,就对其retain()一次,同时在不需要它的时候,就要对其进行release()操作,这一点在引擎很多地方有示例,例如CCNode进行addchild()操作时,会对child进行retain()表示对其引用:
而在removeChild()的时候会对其进行release()操作
第二点是对象的autorelease()操作,该操作的效果是对象在当前这一帧被new出来之后,在下一帧之前没有被执行retain()(例如被add到某个CCMutableArray,或者被一个父Node作为子节点,也可以是我们手动retain()),那么这个对象在下一帧就会被引擎给delete掉,那么有时候出现的空指针错误,可能就来源于此。还有的时候,是在自己手动retain()后放入autorelease池,这样引擎只能将对象的引用减一,而不能delete掉,从而造成内存泄露。最后说一下,本人也是刚开始学习c++和cocos2d-x,写这篇博客与大家分享一下自己的心得,也是希望能帮到一部分对cocos2d内存管理有困惑的朋友,让大家能共同学习进步,同时对我所讲到的不合理的地方,希望各位大牛能够指出,感谢阅读!
cocos2d-x学习笔记18:内存管理01
一、概述
cocos2d-x最初移植自cocos2d的objective C版本。因此,在内存管理上,使用了和NSObject类似的引用计数器方法,相关接口放置在CCObject类中。
二、引用计数器——手动管理内存
CCObject的及其子类的对象在创建时,引用计数自动设置为1。之后每次调用retain,引用计数+1。每次调用release,引用计数-1;若引用计数=0,则直接delete
this。
相关接口如下:
//引用次数+1
virtual void CCObject::retain(void);
//引用次数-1;若引用计数器=0,则delete this;
virtual void CCObject::release(void);
//helper方法,快速判断当前对象只有唯一引用
bool CCObject::isSingleRefrence(void);
//返回引用次数
unsigned int CCObject::retainCount(void);
[align=left]原则1:谁生成(new、copy)谁负责release。[/align]
[align=left]例子:[/align]
CCObject *obj=new CCObject;
...
obj->release();
retain是在指针传递和赋值时使用的,他的含义是表示拥有。这经常用在指针赋值上。
原则2:谁retain,谁负责release。
例子:
obj->retain();
...
obj->release();
[align=left]原则3:传递赋值时,需要先retain形参,后release原指针,最后赋值。(注意,因为这里没有使用自赋值检查,所以这组顺序不能错。)[/align]
[align=left]例子:[/align]
void CCNode::setGrid(CCGridBase* pGrid)
{
CC_SAFE_RETAIN(pGrid);
CC_SAFE_RELEASE(m_pGrid);
m_pGrid = pGrid;
}
[align=left] [/align]
三、自动释放池——自动管理内存
原则4:对于使用autorelease的对象,不必管它,每帧结束后会自动释放。
相关接口:
CCObject* CCObject::autorelease(void);
例子:
CCObject *obj=new CCOjbect;
obj->autorelease();
...
完全手动管理内存,很繁琐,cocos2d-x提供了自动释放池CCPoolManager。将对象置于自动释放池中,每帧绘制结束,就自动release池中的对象。
四、CCNode节点管理
cocos2d-x使用节点组成一棵树,渲染的时候要遍历这棵树。CCNode是所有节点类的父类,他内部使用了一个CCArray对象管理他的所有子节点,当对象被添加为子节点时,实际上是被添加到CCArray对象中,同时会调用这个对象的retain方法。同理,从CCArray中移除时,也会调用release方法。
相关接口:
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);
在切换场景时,系统会遍历整棵树的节点,进行release。
五、静态工厂
cocos2d-x中存在大量的静态工厂方法,这些方法中,全都对this指针调用了autorelease函数。如CCSprite中的这些方法:
static CCSprite* spriteWithTexture(CCTexture2D *pTexture);
static CCSprite* spriteWithTexture(CCTexture2D *pTexture, const CCRect& rect);
static CCSprite* spriteWithTexture(CCTexture2D *pTexture, const CCRect& rect, const CCPoint& offset);
static CCSprite* spriteWithSpriteFrame(CCSpriteFrame *pSpriteFrame);
static CCSprite* spriteWithSpriteFrameName(const char *pszSpriteFrameName);
static CCSprite* spriteWithFile(const char *pszFileName);
static CCSprite* spriteWithFile(const char *pszFileName, const CCRect& rect);
static CCSprite* spriteWithBatchNode(CCSpriteBatchNode *batchNode, const CCRect& rect);
这些方法内部实现了:内存分配、初始化、设置autorelease。用静态工厂来生成对象,可以简化代码,是官方建议的方法。
六、cache机制类
cocos2d-x中存在一些cache类,这些都是单例类的管理器。
CCAnimationCache
CCSpriteFrameCache
CCTextureCache
这些cache内部也使用了ratain和release方法,防止这些资源被释放掉。
使用这些cache,我们可以保存预加载的一些资源,在方便的时候调用它,去绑定给一些对象。注意,这些cache在场景切换时,不会自动删除,需要手动调用purgeXXXX方法,进行清理。
cocos2d-x初探学习笔记(13)--内存回收机制:
之前提到过的内存回收机制,但是不是很全面,这篇主要写一下和autorelease的区别,使用autorelease可以简化我们的内存管理,等于将我们的对象放入自动释放池,放入自动对象池的对象会记着你在晚些时候发送的释放信息,因为忘记释放内存有可能会导致内存泄露,当然这样做也有一个缺点,就是当下一帧我们需要再次使用这个对象的时候,我们就会发现我们的对象已经被释放了,就算你把对象作为成员变量,都无法保证不会出现这种错误,这时你有两种办法,第一种办法就是使retain方法保留,第二种办法就是在addchild的时候,加上tag,然后再当我们需要的时候,我们就可以通过tag再次获得相应的对象即可。
虽然苹果开发者文档建议较少的使用自动释放对象,但是这样做可以方便我们进行内存管理。
和oc一样,cocos2d引擎对于内存管理有些约束,如果你拥有一些对象,你必须在用完之后释放他,如果你使用了autorelease你就不能使用release
使用自动释放的例子
使用释放的例子
使用保留的例子
刚开始研究此引擎,如有错误之处,希望大家多多指正
cocos2d-x 内存释放的机制和使用要注意的地方::
因为功能和接口和objective-c版本的差不多,所以在内存管理上也采用objective-c引用计数的机制来实现内存管理。仔细看了一下cocos2d-x的源代码,确实写的很好,代码组织得很工整。它们所有类都是继承自CCObject,
CCObject有retain(), release()和autorelease()等方法,和objective-c上的NSObject用法一致。每当CCObject对象初始化时它的引用计数reference-count为1, 调用retain()方法reference-count加1, 调用release()方法reference-count减1,当reference-count为0时释放该对象内存。autorelease()方法的作用是对象放进CCAutoreleasePool中进行管理,每当一次绘图结束后,CCPoolManager会对当前的内存池每个对象调用release()方法,一些reference-count为0的对象就会被内存释放。
每当通过new, copy方法获取一个对象时,都有义务在不用它的时候调用release()方法,如果我们是从其他方法中(例如静态方法)就不要调用release()方法,除非之前调用了retain()方法来表示要拥有这个对象一段时间。自定义每个类里的成员变量如果是继承自CCObject,在赋新值之前要先对新值retain(), 然后对旧值release(),这两个步骤不能省去或者调转。定义方法要返回一个对象时,如果该对象是在方法里通过new,
copy来新建的,在返回该对象之前要调用autorelease()方法,例如很多类的静态方法返回它的实例时,都已经调用了autorelease()方法。
只要严格遵守以上规范,内存泄漏的问题应该是可以避免的,以后写c++程序时候,即使没有使用cocos2d-x, 都可以考虑引入通过引
用计数来管理内存这一套机制,既简单又有效。
相关文章推荐
- cocos2d-x 3.0 内存管理机制
- Cocos2d-x 3.1 内存管理机制
- .Net内存管理机制详解
- 初窥Cocos2d-x内存管理机制(1)
- cocos2d-x内存管理机制------沈大海
- object-c(oc)内存管理机制详解
- cocos2d-x 内存管理机制
- cocos2d-x内存管理机制解析
- Cocos2d-x 3.0 内存管理机制
- Cocos2d-x 3.1 内存管理机制
- JVM内存管理机制--运行时数据区域(详解)
- cocos2d-x内存管理机制解析(一)
- cocos2d-x内存管理机制剖析
- 详解JVM内存管理机制
- cocos2d-x 内存管理机制
- Cocos2d-x 内存管理机制
- cocos2d-x 3.0 内存管理机制
- cocos2d-x源码分析::内存管理机制
- cocos2d-x内存管理机制-沈大海cocos2d-x教程6
- bombing:cocos2d-x内存管理机制-侧重调用