本章节介绍如下:
1.自动释放机制
2.几个常用的宏,属性的get/set
这篇的内容是上一篇中遗留下来的,在介绍自动内存释放机制之前我们还得介绍一下Cocos2d-x中的引用计数机制。上篇中的引用计数机制是我们模仿Cocos2d-x写的,现在我们来跟踪下源码,更加升入的理解一下Cocos2d-x的机制
好了,废话有点多了,现在我们上代码,
HelloWorld.cpp中的init方法改成如下:
CCSprite* sprite= new CCSprite();
sprite->initWithFile("Icon.png");
CCLOG("%d",sprite->retainCount());
this->addChild(sprite);
CCLOG("%d",sprite->retainCount());
sprite->release();
CCLOG("%d",sprite->retainCount());
我们来分析一下,
1.我们自己采用NEW创建了一个精灵(不是CCSprite::create())
(首先我们创建精灵,精灵是继承CCObject的,我们找到CCObject的构造函数,看看其中是不是有
CCObject::CCObject(void)
: m_nLuaID(0)
, m_uReference(1) // when the object is created, the reference count of it is 1
, m_uAutoReleaseCount(0)
{
static unsigned int uObjectCount = 0;
m_uID = ++uObjectCount;
}
表示我们已经将引用计数初始为1了)
2.设置了精灵图片
3.输出计数是1
4.我们将精灵增加到层中
(接着,我们光标定位到 this->addChild(sprite); addChild上,按F12,一直跟进去,其中有一句 this->insertChild(child, zOrder);
在F12进入方法 insertChild 中,在 ccArrayAppendObjectWithResize ,在 ccArrayAppendObject 这里比较深,不是视频,所以不知道怎么表达。
一直进来之后我们将会看到
void ccArrayAppendObject(ccArray *arr, CCObject* object)
{
CCAssert(object != NULL, "Invalid parameter!");
object->retain();
arr->arr[arr->num] = object;
arr->num++;
}
所以,addChild方法会给引用计数加1,object->retain(); 懂???这里计数是2
)
5.输出计数是2
6.释放精灵
(这里不用介绍了吧??)
7.输出计数是1
现在问题来了,打起精神哦
最终,在我们代码执行完后,最后输出的计数是1,说明我们的精灵其实是没有真正释放的。所以有内存浪费了,但是我们这样写是没问题的,不信你自己释放两次看看是不是会出错???
原因就在于,我们释放了之后,其实最后层还会调用我们创建的精灵来进行渲染,如果这时候你释放完了,那么渲染的时候将找不到我们的精灵,所以会报错,那么精灵真正释放是在什么时候呢??
还记得我们跟踪addChild方法的时候又insertChild这个方法吗?
有这么一句话 ccArrayAppendObjectWithResize(m_pChildren->data, child);
我们理解的其实就是讲child添加到m_pChildren数组中,这是在CCNode类中,我们的精灵也是继承与它的
那么我们来看看CCNode类的析构函数
if(m_pChildren && m_pChildren->count() > 0)
{
CCObject* child;
CCARRAY_FOREACH(m_pChildren, child)
{
CCNode* pChild = (CCNode*) child;
if (pChild)
{
pChild->m_pParent = NULL;
}
}
}
// children
CC_SAFE_RELEASE(m_pChildren);
所以说最终我们的精灵是在CCNode析构的时候释放的,注意这里有个宏 CC_SAFE_RELEASE 这其实就是安全释放,其代码按F12能看到,就不介绍了,removeChild() 引用计数减1 有兴趣的自己跟踪一下源码。。。
总算是把引用计数介绍完了,理解了没??没理解的话可不行哦,自己多跟踪几遍
1.我们接下来介绍自动释放池:
我们改写INIT方法
bool HelloWorld::init()
{
//
// 1. super init first
if ( !CCLayer::init() )
{
return false;
}
CCSprite* sprite= CCSprite::create("Icon.png");
CCLOG("%d",sprite->retainCount());
this->addChild(sprite);
CCLOG("%d",sprite->retainCount());
return true;
}
将会看到输出:
1
2
呵呵,我知道大家在想什么,那么按照你们的想法,在return true;之前加上这么一句??
sprite->release()
报错??
原因在这,我们进入create方法中
autorelease方法,看名字就知道是自动释放,至于原理是什么?自己有时间跟踪一下吧,我C++不是很懂,跟踪得云里雾里的
总之只要记住,调用了自动释放的方法之后我就不需要手动管理了
还有一点点这节就完了,不要睡觉哦
2. 常用的get/set宏
不管是.net还是Java,我想大家对get、set不陌生吧?.net很方便 直接prop然后两下Tab键就可以了,Eclipse中也有快捷键,只是两种语言表达的方式不一样,java是提供出一个get+字段名的方法和set+字段名的方法,呃!表达的不清楚,不过搞Java的挨踢懂的。
.net下就如public int age{get;set;}
C++中就苦逼了,没有快捷键生成,所以Cococs2d-x提供了几个宏,好了 我们打开
E:\Cocos2D-XTool\cocos2d-x-2.2.3\cocos2dx\platform\CCPlatformMacros.h
这个文件
Ctrl+F 搜索CC_SYNTHESIZE
这里我们就稍微介绍几个常用的,当然,还有很多在这个文件里面,有时间在慢慢摸索吧
CC_SYNTHESIZE
我们在新建项目,在添加如下两个文件
头文件代码
源文件代码
HelloWorldScene.cpp
我们是不是没有在源文件中写setNumber等方法??这就是宏的作用,使我们能少些很多代码
现在我们将代码改成如下
分析一下 首先我们将sPrite赋值给成员变量Number,Number这时候执行一块内存空间,接下来我们在把sPrite2赋值给它
现在Number指向sPrite2,而sPrite此时并没有释放,所以会发生内存泄露,当然我们可以手动释放,但是项目大了的时候,谁能注意到这些??
就算我们用CCSprite::create的方式创建的精灵,内存不需要我们管,但是它的autorelse不是马上就释放的,而是要等到另一条消息进入的时候它才遍历没用的进行释放。所以内存泄露还是会产生
Cocos2d-x就为我们准备了另外一个宏
我们换成CC_SYNTHESIZE_RETAIN就可以了,自己理解一下用红色圈圈圈起来的代码,
1.首先我们将sPrite赋值
2.这是将判断,条件成立,执行CC_SAFE_RETAIN(var), var就是我们的形参sPrite,此时sPrite的计数为2了
3.然后再CC_SAFE_RELEASE,我们没有给Number初始化,所以为null ,没有内存空间,如果直接number->release()会报错的,
这里我们用了安全释放的宏,自己可以看一下源码
4.这是我们把形参赋给Number,此时Number和sPrite执行一块区域,刚好计数又是2.
5.接下来我们把sPrite1赋值,
6.sPrite1执行了RETAIN 计数为2
7.Number relese 计数是1,因为sPrite还在
8.sPrite1和Number计数为2
总算是介绍完了,总结
1.自动释放,只需要调用一下autorelese就可以了
2.注意一下get/set宏,以及CC_SYNTHESIZE_RETAIN和其它的区别