1.class 图地址下载:
http://download.csdn.net/detail/l695863436/5965529
现在我们来分析以下代码:
CCScene *scene = CCScene::create();
运行的时序图地址:
http://download.csdn.net/detail/l695863436/5965529
CCScene::create()函数如下:
单步调试。先会调用CCScene()的构造函数:
代码如下:
其中bool m_bIgnoreAnchorPointForPosition; ///< true if the Anchor Point will be (0,0) when you position the CCNode, false otherwise. Used by CCLayer and CCScene.
m_bIgnoreAnchorPointForPosition = true; // CCLayer和CCScene 设置描点
m_bIgnoreAnchorPointForPosition = flase; // 忽略描点
对象创建成功后 将调用:
pRet->init()
我们找到pRet->init()的代码如下:
我们先看看这行代码:
pDirector = CCDirector::sharedDirector();
static CCDisplayLinkDirector *s_SharedDirector = NULL;
由于CCDisplayLinkDirector继承CCDirector所以可以返回父类指针,现在我们来看:
CCDisplayLinkDirector(void)
: m_bInvalid(false)
{}
其中m_bInvalid = false; // 我是这么理解的,导演可以开工,也就是所以的动作执行有效。
m_bInvalid = true; // 我是这么理解的,导演不能开工,所有的动作无效。
来看看这句代码:
s_SharedDirector->init(); //抱歉:每个数据成员代表什么意思没有仔细研究。
该函数初始化导演类需要的数据,如:FPS,是否可以响应触屏等等信息。
场景类创建并初始化完了后,开始调用
pRet->autorelease(); // 自动管理内存,coco的内存管理是这篇博客重点分析的对象。
好的,我们转到定义处,发现他调用的是CCObject::autorelease
2.内存管理:
该内存管理是通过栈的形式进行管理的。栈中存放的元素是CCAutoreleasePool对象的指针,存放该元素的容器栈是m_pReleasePoolStac,其中m_pCurReleasePool永远是指向栈顶。
现在我们的问题:
(1)我们的对象如场景对象,精灵对象等等,怎样与要存放的元素CCAutoreleasePool 相关联起来。
(2)容器栈是怎么定义的。
我们先来看看从整体来看看。
主要的类成员如下:
CCPoolManager::sharedPoolManager()->addObject(this);
把刚刚创建的场景类添加到CCPoolManager对象中 。注:CCPoolManager是单列。
找到CCPoolManager::addObject定义:
先获取当前释放内存池
如果当前自动释放内存池不为空,
则把场景对象存入自动释放内存池中。
现在我们来看看是怎么存放进去的。我们找到push函数:
原来是先new栈元素,并把该栈顶指针新创建的也就是pPool对象,
m_pReleasePoolStack->addObject(pPool);
getCurReleasePool()->addObject(pObject);
获取到了栈顶指针,现在开始往栈顶元素中添加对象。
以上解决了问题(1)中的问题。
现在我们来解决问题(2),我们回到原来的代码
问题(2)才是核心内容。
还是看push()函数:
CCAutoreleasePool* pPool = new CCAutoreleasePool();
找到构造函数的定义:
我们先看看CCArray的构造函数,
其中data数据类型的定义如下:
默认的容器大小为1;
接着看initWithCapacity的定义:
ccArrayFree(data);首先释放内存。
data = ccArrayNew(capacity);
先申请了一个ccArray的结构体memory。
后面在为结构体的数据成员开辟内存空间。
arr->arr = (CCObject**)calloc(capacity, sizeof(CCObject*));
大家可以去网上找带你资料了解下calloc函数的用法。
我的理解如下:
申请了capacity 个大小sizeof(CCObject*)的内存,并把返回的地址转换为(CCObject**)指针。
现在举例:capacity = 4. 注意:sizeof(CCObject*) = 4
为了更好的理解我还是画一个图吧:
arr->arr地址0x120;
注:其实calloc会自动初始化内存。也就是为0.只是为了更形式的理解我才把数据写上
可以看出calloc申请的内存大小其实就是16字节。
但是要注意的是,objec1~object4都是指向CCObject对象的指针。
arr->num = 0; //代表当前已经存放的对象个数。
arr->max = capacity;//最多能存放元素的个数。
现在我们知道了内存通过什么形式来存储对象的。
m_pManagedObjectArray->init();
初始化容器。也就是上面讲得new内存。其实这里我也觉得奇怪,
在 m_pManagedObjectArray = new CCArray();其实已经初始化了内存,
我认为 m_pManagedObjectArray->init();这句代码是多余额,可以删掉。我删除了这行代码,运行没有问题。
现在应该清楚了栈元素的数据结构吧,其实就是ccArray:
现在来看看m_pReleasePoolStack数据成员:
CCArray* m_pReleasePoolStack;
也是一个动态数组类。
我们现在来理一理 ccArrayAppendObjectWithResize(data, object);参数data到底是指哪个类的数据成员。
通过CCPoolManager* CCPoolManager::sharedPoolManager() 调用的都是单列。
从构造函数中可以看出,m_pReleasePoolStack跟m_pCurReleasePool生存方法一样,
只是作用不一样。
所以调用的ccArrayAppendObjectWithResize(data, object);data是m_pReleasePoolStack成员的数据。Object才是m_pCurReleasePool对象。
现在我们把入栈的图画出来:
注: 容器大小为1.
备注:object = 0代表没有对象。
添加了一个元素进来。图如下:
备注:这里只是为将要添加的对象,申请了一个栈元素的空间。
后面我就要填充Object = 0 的对象。
该函数就是在如果装满了,就开max = max *2的buf.
现在来看看这个push函数中的最后一句代码。
pPool->release();
我们可以从CCObject的构造函数中看出
CCAutoreleasePool* pPool = new CCAutoreleasePool(); //ref = 1 即m_uReference = 1
在该函数 m_pReleasePoolStack->addObject(pPool); //ref = 2
会调用 object->retain(); 即m_uReference = 2
最后的栈结果如下:
m_pReleasePoolStack入栈后
所以内存管理,变动的只是Object = m_pCurReleasePool,
如果要在添加一个元素如下图:
m_pReleasePoolStack入栈后
后面继续,先休息下。写了一天了。
有问题欢迎大家提出。