cocos2d-x源于cocos2d-iphone,为了与Objective-c一致,cocos2d-x也采用了引用计数与自动回收的内存管理机制。
要现实自动内存回收,需继承于cocos2d-x的根类CCObject。当然自动释放会影响性能的。
cocos2d-x中有很多静态工场方法,例如以create开头的,这些静态工场方法创建的对象都使用的autorelease,试想如果不用autorelease,
CCObject* create(){CCObject *ret = new CCObject(); return ret;}
此时函数内的引用在函数结束时就结束了,但是对方没有被释放,如果返回的ret没有被用户引用或者释放,那么就造成了内存泄露。所以加入autorelease是个不错的解决方法。
看看CCObject的这两个字段和方法:
@H_404_38@protected: // 引用计数 unsigned int m_uReference; // 自动释放次数 以前是bool m_bManaged;标识是否采用自动释放 unsigned int m_uAutoReleaseCount; public: void release(void); void retain(void); CCObject* autorelease(void); unsigned int retainCount(void);
有这么一个原则:谁引用谁retain和release,对象传值时先retain再release(避免自己给自己传值时,先release可能会释放对象),调用release方法,当引用计数为0时,就会delete此对象。
内存管理autorelease是怎么实现的呢,进入autorelease看到调用了下面这个函数:
CCPoolManager::sharedPoolManager()->addObject(this);将对象加入回收池
@H_404_38@void CCAutoreleasePool::addObject(CCObject* pObject) { m_pManagedObjectArray->addObject(pObject); CCAssert(pObject->m_uReference > 1,"reference count should be greater than 1"); ++(pObject->m_uAutoReleaseCount); pObject->release(); // no ref count,in this case autorelease pool added. }
函数中将对象加入管理数组中,再对自动释放计数+1,然后release使引用计数-1。
当create一个对象时,new使引用计数为1,调用autorelease后,加入回收池,然后release引用计数-1。那么此时引用计数为0吗?
答案肯定不是,如果为0,此时该对象就会被delete。cocos2d-x中的数据结构转为这种内存管理设置,如CCArray,当对array调用addObject时,就会调用retain使引用计数加1,当array调用removeObject时就会release。那么当加入回收池后,此时的这个引用归回收池所有,当一帧结束释放回收池的时候,回收池中的所有对象都会调用release。如果此时没有其他对象引用该对象,则该对象会被删除。
来看看主循环mainLoop中的调用:
@H_404_38@ if (m_bPurgeDirecotorInNextLoop) { m_bPurgeDirecotorInNextLoop = false; purgeDirector(); } else if (! m_bInvalid) { drawScene(); // release the objects CCPoolManager::sharedPoolManager()->pop(); }
每一帧结束调用sharedPoolManager()->pop()此时栈顶的回收池就被释放,
void CCPoolManager::pop() { if (! m_pCurReleasePool) { return; } int nCount = m_pReleasePoolStack->count(); m_pCurReleasePool->clear(); if(nCount > 1) { //释放栈顶的回收池 m_pReleasePoolStack->removeObjectAtIndex(nCount-1); m_pCurReleasePool = (CCAutoreleasePool*)m_pReleasePoolStack->objectAtIndex(nCount - 2); } }
此时池中的所有对象都会被release,如果没有其它的对象(场景、层、数组等)在引用他们,这些对象就会被delete。
而新一帧开始后,第一个调用autorelease的对象在内部调用getCurReleasePool()时,如果栈中没有回收池,就会push一个回收池,sharedPoolManager()->push()。
CCAutoreleasePool* CCPoolManager::getCurReleasePool() { if(!m_pCurReleasePool) { push(); } CCAssert(m_pCurReleasePool,"current auto release pool should not be null"); return m_pCurReleasePool; }
可以看出每一帧回收池都要建立、删除,而且添加对象进入回收池,那么性能方面当然会受到影响,当回收池中对象很多时,表现得很明显。
方法2:手动释放并创建一个回收池
CCPoolManager::shardPoolManager()->push(); for(int i=0; i!=n; ++i) { objArray[i]->autorelease(); } CCPoolManager::shardPoolManager()->pop();
这样就不会让自动释放的对象集中在一帧结束的时候。