1. 寫在前面
C++在C11之前,都是使用new分配內(nèi)存,delete釋放內(nèi)存。這看起來是不是非常輕松?其實這會暴露出許多缺點:
- 內(nèi)存重復(fù)釋放
- 野指針:指向的內(nèi)存已經(jīng)被釋放了,但是指針還在使用
- 內(nèi)存泄露:不再使用的內(nèi)存沒有釋放,內(nèi)存占用率過高
C11就引出了智能指針來解決以上的三個問題。
我已經(jīng)有博客提到智能指針了,想多了解的朋友可以去看看:C++多線程下的shared_ptr、C11新特性之智能指針。
shared_ptr:多個智能指針可共享同一內(nèi)存,使用引用計數(shù)
unique_ptr:不能和其他智能指針指向同一個內(nèi)存,直接賦值給其他指針會報錯,但是可以使用move來轉(zhuǎn)移,轉(zhuǎn)移后之前的智能指針就失效。
week_ptr:使用lock()函數(shù),它可以檢測weak_ptr訪問的對象是否存在
既然智能指針看起來解放了大家去釋放內(nèi)存的步驟,那么為什么在cocos里面我們不使用智能指針來管理內(nèi)存呢?
- 性能損失
- 仍要顯示聲明指針
2. Ref
cocos2dx雖然沒有使用智能指針,但是它用了一個與智能指針非常相似的東西,也就是下面我們要說的Ref。
Ref的主要流程:
當對象被創(chuàng)建時候,引用計數(shù)為1。為了保證對象的存在,可以調(diào)用retain函數(shù)保持對象,retain會使其引用計數(shù)加1,如果不需要這個對象可以調(diào)用release函數(shù),release使其引用計數(shù)減1。當對象的引用計數(shù)為0的時候,引擎就知道不再需要這個對象了,就會釋放對象內(nèi)存。
class Ref:
def retain():pass 增加引用
def release():pass 減少引用
def autorelease():pass 交給自動釋放池
def getRefreenceCount():pass
self._referenceCount = 0
#friend class AutoreleasePool
在使用Node節(jié)點對象時候,addChild函數(shù)可以保持Node節(jié)點對象,使引用計數(shù)加1。通過removeChild函數(shù)移除Node節(jié)點對象,使引用計數(shù)減1。
2.1 Ref::retain()
retain()對其引用計數(shù)加1
void Ref::retain()
{
CCASSERT(_referenceCount > 0, "reference count should be greater than 0");
++_referenceCount;
}
2.2 Ref::release()
release()先對引用計數(shù)-1,然后判斷引用計數(shù)是否為0,若為0則刪除對象
void Ref::release()
{
CCASSERT(_referenceCount > 0, "reference count should be greater than 0");
對其引用計數(shù)值進行-1
--_referenceCount;
引用計數(shù)為0,刪除對象
if (_referenceCount == 0)
{
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
auto poolManager = PoolManager::getInstance();
if (!poolManager->getCurrentPool()->isClearing() && poolManager->isObjectInPools(this))
{
CCASSERT(false, "The reference shouldn't be 0 because it is still in autorelease pool.");
}
#endif
從保存Ref*的list中刪除
#if CC_REF_LEAK_DETECTION
untrackRef(this);
#endif
刪除該對象
delete this;
}
}
2.3 Ref::autorelease()
autorelease()將節(jié)點添加到自動釋放池中,它由PoolManager進行管理。
調(diào)用內(nèi)存管理PoolManager的單例對象中存儲的自動釋放池對象的addObject函數(shù)加入當前的Ref實例對象的指針
cocos2dx為了不用顯式調(diào)用AutoRelease,一般讓程序員通過create來獲取對象,而create里幫我們調(diào)用好了AutoRelease。autorelease主要應(yīng)用在ui元素上。
Ref* Ref::autorelease()
{
PoolManager::getInstance()->getCurrentPool()->addObject(this);
return this;
}
3. CCAutoreleasePool
這個類主要含有下面幾個方法
void addObject(Ref *object); 將Ref添加到自動釋放池
void clear(); 清除自動釋放池所有對象
bool contains(Ref* object) const; 檢測自動釋放池中是否有Object對象
void dump(); 打印刪除的對象的地址
3.1 構(gòu)造函數(shù)
構(gòu)造函數(shù)里可以看出,每一個autoreleasePool對象都有一個管理obj的隊列managedObjectArray用于存放obj,并且所有Pool對象都是放在poolManage的單例對象的堆中。
AutoreleasePool::AutoreleasePool()
: _name("")
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
, _isClearing(false)
#endif
{ 容器擴容,申請保存ref的vector的size增加150
_managedObjectArray.reserve(150);
把AutoreleasePool添加到管理類生明的vector中
PoolManager::getInstance()->push(this);
}
3.2 析構(gòu)函數(shù)
析構(gòu)函數(shù)是先清除自動釋放池所有obj對象,然后再將自己從poolManage的堆中彈出
AutoreleasePool::~AutoreleasePool()
{
CCLOGINFO("deallocing AutoreleasePool: %p", this);
clear();
PoolManager::getInstance()->pop();
}
3.3 addObject
把object添加到managedObjectArray自動釋放池
void AutoreleasePool::addObject(Ref* object)
{
_managedObjectArray.push_back(object);
}
3.4 clear
clear就是將managedObjectArray里的所有obj都執(zhí)行release
void AutoreleasePool::clear()
{
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
_isClearing = true;
#endif
std::vector<Ref*> releasings;
releasings.swap(_managedObjectArray);
//遍歷自動釋放池managedObjectArray里存放的所有的Ref
for (const auto &obj : releasings)
{
//調(diào)用obj的release(),對obj的引用計數(shù)-1(如果對象引用計數(shù)為0則刪除)
obj->release();
}
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
_isClearing = false;
#endif
}
4. PoolManager
- PoolManager類對象是一個單例對象singleInstance ,整個工程中只有一個
- PoolManager類維護著一個堆releasePoolStack,用來存放工程中所有的AutoreleasePool對象;
- 名字叫"cocos2d autorelease pool"的自動釋放池對象保存工程中大部分渲染的節(jié)點以及我們通過create()創(chuàng)建的對象,這個AutoreleasePool對象是工程剛啟動時就生成的;
4.1 順便通過PoolManager來重新學(xué)一下
static PoolManager* s_singleInstance; //poolmanager的實例,再強調(diào)一遍這是個單例對象
PoolManager* PoolManager::s_singleInstance = nullptr;
PoolManager* PoolManager::getInstance()
{
//判斷是否存在singleInstance
if (s_singleInstance == nullptr)
{
//如果singleInstance 為nullptr,new一個PoolManager單例
s_singleInstance = new (std::nothrow) PoolManager();
//new一個AutoreleasePool并取名字為"cocos2d autorelease pool"
new AutoreleasePool("cocos2d autorelease pool");
}
return s_singleInstance;
}
void PoolManager::destroyInstance()
{
delete s_singleInstance;
s_singleInstance = nullptr;
}