前两篇博文我们我们详细分析了cocos2dx的内存管理机制的实现,现在我们在这里简单总结一下:
对于一个对象来说,一般流程:
- Object obj=new Object() ,自引用计数为1
- 然后调用obj.autorelease()方法,进行自动释放,将obj添加到自动释放池,并保持自引用计数为1
- 在这里可以对obj引用,比如将obj添加到一个Layer中,则Layer此时是obj的使用者
- 然后程序会在主循环去判断,绘制第一帧的时候,解除自动释放池对obj的引用(release操作),将obj交给使用者管理
- 所谓交给使用者管理,便是链式反应,即释放一个对象,那么他所引用的对象也会被释放
之前我们分析的时候,知道自动释放池是交给CCPoolManager来管理的,那么现在来看一下这个类:
class CC_DLL CCPoolManager { CCArray* m_pReleasePoolStack; CCAutoreleasePool* m_pCurReleasePool; CCAutoreleasePool* getCurReleasePool(); public: CCPoolManager(); ~CCPoolManager(); void finalize(); void push(); void pop(); void removeObject(CCObject* pObject); void addObject(CCObject* pObject); static CCPoolManager* sharedPoolManager(); static void purgePoolManager(); friend class CCAutoreleasePool; };
这里我们关注一下addObject方法
void CCPoolManager::addObject(CCObject* pObject) { getCurReleasePool()->addObject(pObject); }
getCurReleasePool():
CCAutoreleasePool* CCPoolManager::getCurReleasePool() { if(!m_pCurReleasePool) { push(); } CCAssert(m_pCurReleasePool, "current auto release pool should not be null"); return m_pCurReleasePool; }push()方法:
void CCPoolManager::push() { CCAutoreleasePool* pPool = new CCAutoreleasePool(); //ref = 1 m_pCurReleasePool = pPool; m_pReleasePoolStack->addObject(pPool); //ref = 2 pPool->release(); //ref = 1 }
我们发现,,当我们将对象添加到自动释放池的时候,也就是调用addObject方法,会首先判断是否有当前的释放池,如果没有则创建,如果有,则直接使用,可想而知,在任何使用,任何情况,通过 addObject 只需要创建一个释放池便已经足够使用了。事实上也是如此。
然后再看pop方法:
void CCPoolManager::pop() { if (! m_pCurReleasePool) { return; } int nCount = m_pReleasePoolStack->count(); m_pCurReleasePool->clear(); if(nCount > 1) { m_pReleasePoolStack->removeObjectAtIndex(nCount-1); // if(nCount > 1) // { // m_pCurReleasePool = m_pReleasePoolStack->objectAtIndex(nCount - 2); // return; // } m_pCurReleasePool = (CCAutoreleasePool*)m_pReleasePoolStack->objectAtIndex(nCount - 2); } /*m_pCurReleasePool = NULL;*/ }
还记得主循环对自动释放池的pop操作吗,如上,先对自动释放池clear之后,从自动释放池数组中删除该对象自动管理池;然后将数组中的倒数第二个自动释放池作为当前自动释放池。看到这里 我就不解了!什么情况下才能用到多个释放池?按照设计的逻辑根本用不到。带着这个疑问,在push添加一句注释看看
运行程序,发现实际的使用中,push
只被调用了两次!我们知道,通过 addObject 可能会自动调用 push()
一次,但也仅有一次,所以一定是哪里手动调用了 push()
方法,才会出现这种情况,通过查看引擎源码,发现在bool
CCDirector::init(void)中,
有这么一句:
bool CCDirector::init(void) { CCLOG("cocos2d: %s", cocos2dVersion()); ... ... m_dOldAnimationInterval = m_dAnimationInterval = 1.0 / kDefaultFPS; m_pobScenesStack = new CCArray(); m_pobScenesStack->init(); ... ... m_fContentScaleFactor = 1.0f; ... ... // touchDispatcher m_pTouchDispatcher = new CCTouchDispatcher(); m_pTouchDispatcher->init(); // KeypadDispatcher m_pKeypadDispatcher = new CCKeypadDispatcher(); // Accelerometer m_pAccelerometer = new CCAccelerometer(); // 这里手动调用了 push 方法,而在这之前的初始化过程中,间接的使用了 CCObject 的 autorelease,已经触发过一次 push 方法 CCPoolManager::sharedPoolManager()->push(); return true; }
所以我们便能够看到 push 方法被调用了两次,但其实如果我们把这里的手动调用放在方法的开始处,或者干脆就不使用
CCPoolManager::sharedPoolManager()->push();
,对程序也没任何影响,这样从头到尾,只创建了一个自动释放池,而这里多创建的一个并没有多大的用处。 或者用处不甚明显,因为多创建一个释放池是有其效果的,效果具体体现在哪里,那就是 可以使调用
push() 方法之前的对象,多存活一帧。,因为 pop 方法只对当前释放池做了 clear 释放。