cocos2dx內存管理機制學習筆記,源碼分析

cocos2dx面試時最容易考的問題就是內存管理,我自己都被問的煩了,本文分析透徹,源碼詳盡,可以避免在這個問題上失分

cocos2dx的內存管理採用引用計數的策略,百度百科的引用計數解釋如下:

 

通過源代碼分析,作出以下總結:

1.Ref類中的_referenceCount成員變量用作引用計數

protected:
    /// count of references
    unsigned int _referenceCount;

2.Ref的構造函數爲_referenceCount賦值爲1

Ref::Ref()
: _referenceCount(1) // when the Ref is created, the reference count of it is 1

3.retain() 執行++_referenceCount,release()執行--_referenceCount,並且當_referenceCount ==0時,釋放對象.

 

void Ref::retain()
{
    ++_referenceCount;
}
void Ref::release()
{
	--_referenceCount;
	if (_referenceCount == 0)
	{
		delete this;
	}
}

4.autorelease()會在自動釋放池中,添加一個對象(向量尾部插入新對象),AutoreleasePool類中,使用向量容器vector<Ref*> _managedObjectArray保存對象。

Ref* Ref::autorelease()
{
    PoolManager::getInstance()->getCurrentPool()->addObject(this);
    return this;
}
void AutoreleasePool::addObject(Ref* object)
{
    _managedObjectArray.push_back(object);
}
std::vector<Ref*> _managedObjectArray;

5.通過create()創建的對象,會執行new(),init(),autorelease()幾個函數,即對象被放入自動釋放池,這類對象若不addchild()會在下一幀釋放,若想保留可手動retain(),不需要時手動release()

 

Scene* Scene::create()
{
    Scene *ret = new (std::nothrow) Scene();
    if (ret && ret->init())
    {
        ret->autorelease();
        return ret;
    }
    else
    {
        CC_SAFE_DELETE(ret);
        return nullptr;
    }
}

6.導演類的mainLoop()執行drawScene()畫完一幀後,執行 PoolManager::getInstance()->getCurrentPool()->clear();clear()有2個功能:

(1)

std::vector<Ref*> releasings;
releasings.swap(_managedObjectArray);

 

用新向量releasings和向量_managectArray互換,將_managedObjectArray內容清空,長度清0,這麼做是爲了讓自動釋放池中的對象只自動執行一次release()判斷是否釋放

(2)

 

for (const auto &obj : releasings)
{
    obj->release();
}

 

遍歷自動釋放池中的對象逐一調用一次release(),完整代碼如下:

 

void DisplayLinkDirector::mainLoop()
{
    if (_purgeDirectorInNextLoop)
    {
        _purgeDirectorInNextLoop = false;
        purgeDirector();
    }
    else if (_restartDirectorInNextLoop)
    {
        _restartDirectorInNextLoop = false;
        restartDirector();
    }
    else if (! _invalid)
    {
        drawScene();//畫一幀
     
        // release the objects
        PoolManager::getInstance()->getCurrentPool()->clear();
    }
}
void AutoreleasePool::clear()
{
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
    _isClearing = true;
#endif
    std::vector<Ref*> releasings;
    releasings.swap(_managedObjectArray);
    for (const auto &obj : releasings)
    {
        obj->release();
    }
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
    _isClearing = false;
#endif
}

7.addChild(Node *child)會執行child->retain();removechild()會執行child->release();

 

 

void Node::addChild(Node *child)
{
    CCASSERT( child != nullptr, "Argument must be non-nil");
    this->addChild(child, child->_localZOrder, child->_name);
}
void Node::addChild(Node* child, int localZOrder, const std::string &name)
{
    CCASSERT(child != nullptr, "Argument must be non-nil");
    CCASSERT(child->_parent == nullptr, "child already added. It can't be added again");
    
    addChildHelper(child, localZOrder, INVALID_TAG, name, false);
}
void Node::addChildHelper(Node* child, int localZOrder, int tag, const std::string &name, bool setTag)
{
	if (_children.empty())
	{
		this->childrenAlloc();
	}

	this->insertChild(child, localZOrder);
	//以下省略
}
void Node::insertChild(Node* child, int z)
{
    _transformUpdated = true;
    _reorderChildDirty = true;
    _children.pushBack(child);
    child->_localZOrder = z;
}
void pushBack(T object)
{
     CCASSERT(object != nullptr, "The object should not be nullptr");
     _data.push_back( object );
     object->retain();
}

 

 

8.當對象的指針作爲形參時,函數體內需retain()形參 (形參被其他指針保留了,所以需retain),

 

release被賦值指針(被賦值指針不需要使用原先指向的對象了,所以需release),再賦值.

 

void testFun(Ref* obj1)
{
	obj1->retain();
	obj2->release();
	obj2 = obj1;
}

 

面試環節

問:cocos2d-x內存管理機制如何實現?

答:cocos2d-x使用引用計數機制實現內存自動管理。有一個基類Ref,Ref中通過referenceCount變量保存計數,構造函數中會把新建對象的計數賦值爲1,Ref中retain可將計數自加,release可將計數自減,並且判斷當計數爲0時進行釋放。新建出的節點加到父節點時會調用retain,從父節點移除時會調用release。有一個自動釋放池類AutoreleasePool,其中有個autorelease函數,可將對象放到自動釋放池的一個向量容器中,通過CREATE宏創建出來的函數都會執行autorelease操作。在每一幀結束時會調用自動釋放池的clear函數,clear會將自動釋放池中所有對象調用release函數。因此新建出來的對象其引用計數的變化過程爲:創建出來構造函數賦值爲1,加到父節點後變爲2,所在幀結束時由於調用了clear又變爲1這樣一個過程。

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章