之前看过很多的文章,都是讲解cocos2dx的引用计数原理。有好有坏,今天我也自己来写一篇引用计数的文章,并且开始尝试阅读cocos2dx的源码,把自己的收获更新到这个博客,不为多少人看。只为自己记录这一路的点点滴滴。
讲了这么多那么开始吧!
首先就是贴上cocos2dx的万物之源 Ref类,这个类是Node的基类,而所有的显示节点类都是继承自Node类,所以他算是cocos2dx的根。
CCRef.h
class CC_DLL Ref{public: voID retain(); voID release(); Ref* autorelease(); unsigned int getReferenceCount() const;protected: Ref();public: virtual ~Ref();protected: //用于计数的变量 unsigned int _referenceCount; frIEnd class autoreleasePool;#if CC_ENABLE_SCRIPT_BINDINGpublic: unsigned int _ID; int _luaID; voID* _scriptObject;#endif #if CC_USE_MEM_LEAK_DETECTIONpublic: static voID printLeaks();#endif};CCRef.cpp
#include "base/CCRef.h"#include "base/CCautoreleasePool.h"#include "base/ccMacros.h"#include "base/ccScriptSupport.h"#if CC_USE_MEM_LEAK_DETECTION#include <algorithm> // std::find#endifNS_CC_BEGIN#if CC_USE_MEM_LEAK_DETECTIONstatic voID trackRef(Ref* ref);static voID untrackRef(Ref* ref);#endifRef::Ref(): _referenceCount(1) // when the Ref is created,the reference count of it is 1{#if CC_ENABLE_SCRIPT_BINDING static unsigned int uObjectCount = 0; _luaID = 0; _ID = ++uObjectCount; _scriptObject = nullptr;#endif #if CC_USE_MEM_LEAK_DETECTION trackRef(this);#endif}Ref::~Ref(){#if CC_ENABLE_SCRIPT_BINDING // if the object is referenced by Lua engine,remove it if (_luaID) { ScriptEngineManager::getInstance()->getScriptEngine()->removeScriptObjectByObject(this); } else { ScriptEngineProtocol* pEngine = ScriptEngineManager::getInstance()->getScriptEngine(); if (pEngine != nullptr && pEngine->getScriptType() == kScriptTypeJavaScript) { pEngine->removeScriptObjectByObject(this); } }#endif#if CC_USE_MEM_LEAK_DETECTION if (_referenceCount != 0) untrackRef(this);#endif}voID Ref::retain(){ CCASSERT(_referenceCount > 0,"reference count should greater than 0"); ++_referenceCount;}voID Ref::release(){ CCASSERT(_referenceCount > 0,"reference count should greater than 0"); --_referenceCount; 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#if CC_USE_MEM_LEAK_DETECTION untrackRef(this);#endif delete this; }}Ref* Ref::autorelease(){ PoolManager::getInstance()->getCurrentPool()->addobject(this); return this;}unsigned int Ref::getReferenceCount() const{ return _referenceCount;}#if CC_USE_MEM_LEAK_DETECTIONstatic std::List<Ref*> __refAllocationList;voID Ref::printLeaks(){ if (__refAllocationList.empty()) { log("[memory] All Ref objects successfully cleaned up (no leaks detected).\n"); } else { log("[memory] WARNING: %d Ref objects still active in memory.\n",(int)__refAllocationList.size()); for (const auto& ref : __refAllocationList) { CC_ASSERT(ref); const char* type = typeID(*ref).name(); log("[memory] LEAK: Ref object '%s' still active with reference count %d.\n",(type ? type : ""),ref->getReferenceCount()); } }}static voID trackRef(Ref* ref){ CCASSERT(ref,"InvalID parameter,ref should not be null!"); __refAllocationList.push_back(ref);}static voID untrackRef(Ref* ref){ auto iter = std::find(__refAllocationList.begin(),__refAllocationList.end(),ref); if (iter == __refAllocationList.end()) { log("[memory] CORRUPTION: Attempting to free (%s) with invalID ref tracking record.\n",typeID(*ref).name()); return; } __refAllocationList.erase(iter);}#endif // #if CC_USE_MEM_LEAK_DETECTIONNS_CC_END
我们从一看出Ref这个类中有一个
protected: //用于计数的变量 unsigned int _referenceCount;这个变量就是记录引用次数,并且在构造函数中初始化时1。
在代码retain中可以看到只有简单增加这个计数器。
重要的来了,那就是release这个函数,我们看到他是在计数器为0的时候,delete自己。这样的设计源于自己创建自己销毁这个原则。
我们还可以看到 autoRelease 这个接口,他调用了PoolManager。
从字面上猜到,这个接口的作用应该是自动释放。
那么他是怎么实现的呢?
我们又找到了PoolManager的源码。
CCautoreleasePool.h
#ifndef __autoRELEASEPOol_H__#define __autoRELEASEPOol_H__#include <stack>#include <vector>#include <string>#include "base/CCRef.h"NS_CC_BEGINclass CC_DLL autoreleasePool{public: autoreleasePool(); autoreleasePool(const std::string &name); ~autoreleasePool(); voID addobject(Ref *object); voID clear(); #if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0) bool isClearing() const { return _isClearing; };#endif bool contains(Ref* object) const; voID dump(); private: std::vector<Ref*> _managedobjectArray; std::string _name; #if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0) bool _isClearing;#endif};class CC_DLL PoolManager{public: CC_DEPRECATED_ATTRIBUTE static PoolManager* sharedPoolManager() { return getInstance(); } static PoolManager* getInstance(); CC_DEPRECATED_ATTRIBUTE static voID purgePoolManager() { destroyInstance(); } static voID destroyInstance(); autoreleasePool *getCurrentPool() const; bool isObjectInPools(Ref* obj) const; frIEnd class autoreleasePool; private: PoolManager(); ~PoolManager(); voID push(autoreleasePool *pool); voID pop(); static PoolManager* s_singleInstance; std::vector<autoreleasePool*> _releasePoolStack;};NS_CC_END#endif //__autoRELEASEPOol_H__CCautoreleasePool.cpp
#include "base/CCautoreleasePool.h"#include "base/ccMacros.h"NS_CC_BEGINautoreleasePool::autoreleasePool(): _name("")#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0),_isClearing(false)#endif{ _managedobjectArray.reserve(150); PoolManager::getInstance()->push(this);}autoreleasePool::autoreleasePool(const std::string &name): _name(name)#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0),_isClearing(false)#endif{ _managedobjectArray.reserve(150); PoolManager::getInstance()->push(this);}autoreleasePool::~autoreleasePool(){ cclOGINFO("dealLocing autoreleasePool: %p",this); clear(); PoolManager::getInstance()->pop();}voID autoreleasePool::addobject(Ref* object){ _managedobjectArray.push_back(object);}voID autoreleasePool::clear(){#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0) _isClearing = true;#endif for (const auto &obj : _managedobjectArray) { obj->release(); } _managedobjectArray.clear();#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0) _isClearing = false;#endif}bool autoreleasePool::contains(Ref* object) const{ for (const auto& obj : _managedobjectArray) { if (obj == object) return true; } return false;}voID autoreleasePool::dump(){ cclOG("autorelease pool: %s,number of managed object %d\n",_name.c_str(),static_cast<int>(_managedobjectArray.size())); cclOG("%20s%20s%20s","Object pointer","Object ID","reference count"); for (const auto &obj : _managedobjectArray) { CC_UNUSED_ParaM(obj); cclOG("%20p%20u\n",obj,obj->getReferenceCount()); }}//--------------------------------------------------------------------//// PoolManager////--------------------------------------------------------------------PoolManager* PoolManager::s_singleInstance = nullptr;PoolManager* PoolManager::getInstance(){ if (s_singleInstance == nullptr) { s_singleInstance = new PoolManager(); // Add the first auto release pool new autoreleasePool("cocos2d autorelease pool"); } return s_singleInstance;}voID PoolManager::destroyInstance(){ delete s_singleInstance; s_singleInstance = nullptr;}PoolManager::PoolManager(){ _releasePoolStack.reserve(10);}PoolManager::~PoolManager(){ cclOGINFO("dealLocing PoolManager: %p",this); while (!_releasePoolStack.empty()) { autoreleasePool* pool = _releasePoolStack.back(); delete pool; }}autoreleasePool* PoolManager::getCurrentPool() const{ return _releasePoolStack.back();}bool PoolManager::isObjectInPools(Ref* obj) const{ for (const auto& pool : _releasePoolStack) { if (pool->contains(obj)) return true; } return false;}voID PoolManager::push(autoreleasePool *pool){ _releasePoolStack.push_back(pool);}voID PoolManager::pop(){ CC_ASSERT(!_releasePoolStack.empty()); _releasePoolStack.pop_back();}NS_CC_END可以看到
autoreleasePool* PoolManager::getCurrentPool()返回了一个autoreleasePool的指针,<pre name="code" >autoreleasePool这个指针将在Ref autoRelease的时候添加到了一个叫<pre name="code" >_managedobjectArray的vector里面去了。
那么自动管理是怎么做到呢?
转到CCDirector.cpp
<pre name="code" >voID displaylinkDirector::mainLoop(){ if (_purgeDirectorInNextLoop) { _purgeDirectorInNextLoop = false; purgeDirector(); } else if (! _invalID) { drawScene(); // release the objects PoolManager::getInstance()->getCurrentPool()->clear(); }}
在这个文件的上面那个函数中,我们可以看到每个loop里面在渲染场景之后PoolManager做了一个 *** 作,这个 *** 作就是autoreleasePool类里面的clear,
这个clear就是将维护的_managedobjectArray里面的每个对象执行release *** 作,最后把_managedobjectArray清空。
<span >看到这里我们大约知道了他的原理了。</span>
1、我们在创建Ref的时候计数器值是1;
2、执行retain的时候计数器加1,执行release的时候计数器减1,当计数器为0时由对象自己删除自己。
3、当我们执行autorelease的时候,Ref把释放的 *** 作的权利给了autoreleasePool这个友元类,就是如果我们没有任何retain *** 作,这个Ref的对象将在这一帧结束后被autoreleasePool的clear给释放掉。
4、当我们在创建后又retain,那么将保留对这个Ref对象的控制权,只有在你release后才会被释放,因为autoreleasePool只执行一次便不再参与该对象的内存管理。
<span ></span>
<span >差不多我了解的cocos2dx引用计数的原理就是这么多了,博主的水平也是非常有限,哪里写得不好或出错,欢迎指正交流。</span>总结
以上是内存溢出为你收集整理的cocos2dx 3.x 源码阅读之一 引用计数原理全部内容,希望文章能够帮你解决cocos2dx 3.x 源码阅读之一 引用计数原理所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)