从cocos的CCNode.h的可以看出Node是继承与Ref,首先我们先看CCRef.h文件,如下:
//CCRef.h
#include "base/CCPlatformMacros.h"
#include "base/ccConfig.h" #define CC_USE_MEM_LEAK_DETECTION 0 NS_CC_BEGIN /** * @addtogroup base_nodes * @{ */ class Ref; /** Interface that defines how to clone an Ref */ class CC_DLL Clonable { public: /** returns a copy of the Ref */ virtual Clonable* clone() const = 0; /** * @js NA * @lua NA */ virtual ~Clonable() {}; /** returns a copy of the Ref. * @deprecated Use clone() instead */ CC_DEPRECATED_ATTRIBUTE Ref* copy() const { // use "clone" instead CC_ASSERT(false); return nullptr; } }; class CC_DLL Ref { public: /** * Retains the ownership. * * This increases the Ref's reference count. * * @see release,autorelease * @js NA */ void retain(); /** * Releases the ownership immediately. * * This decrements the Ref's reference count. * * If the reference count reaches 0 after the descrement,this Ref is * destructed. * * @see retain,autorelease * @js NA */ void release(); /** * Releases the ownership sometime soon automatically. * * This descrements the Ref's reference count at the end of current * autorelease pool block. * * If the reference count reaches 0 after the descrement,this Ref is * destructed. * * @returns The Ref itself. * * @see AutoreleasePool,retain,release * @js NA * @lua NA */ Ref* autorelease(); /** * Returns the Ref's current reference count. * * @returns The Ref's reference count. * @js NA */ unsigned int getReferenceCount() const; protected: /** * Constructor * * The Ref's reference count is 1 after construction. * @js NA */ Ref(); public: /** * @js NA * @lua NA */ virtual ~Ref(); protected: /// count of references unsigned int _referenceCount; friend class AutoreleasePool; #if CC_ENABLE_SCRIPT_BINDING public: /// object id,ScriptSupport need public _ID unsigned int _ID; /// Lua reference id int _luaID; #endif // Memory leak diagnostic data (only included when CC_USE_MEM_LEAK_DETECTION is defined and its value isn't zero) #if CC_USE_MEM_LEAK_DETECTION public: static void printLeaks(); #endif }; class Node; //定义的回调方式 typedef void (Ref::*SEL_CallFunc)(); typedef void (Ref::*SEL_CallFuncN)(Node*); typedef void (Ref::*SEL_CallFuncND)(Node*,void*); typedef void (Ref::*SEL_CallFuncO)(Ref*); typedef void (Ref::*SEL_MenuHandler)(Ref*); typedef void (Ref::*SEL_SCHEDULE)(float); #define callfunc_selector(_SELECTOR) static_cast<cocos2d::SEL_CallFunc>(&_SELECTOR) #define callfuncN_selector(_SELECTOR) static_cast<cocos2d::SEL_CallFuncN>(&_SELECTOR) #define callfuncND_selector(_SELECTOR) static_cast<cocos2d::SEL_CallFuncND>(&_SELECTOR) #define callfuncO_selector(_SELECTOR) static_cast<cocos2d::SEL_CallFuncO>(&_SELECTOR) #define menu_selector(_SELECTOR) static_cast<cocos2d::SEL_MenuHandler>(&_SELECTOR) #define schedule_selector(_SELECTOR) static_cast<cocos2d::SEL_SCHEDULE>(&_SELECTOR) // end of base_nodes group /// @} NS_CC_END
#endif // __BASE_CCREF_H__
从文件里面看到了release,autorelease这些与cocos内存管理有关的函数,现在在分析一下实现的文件CCRef.cpp
//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 #endif NS_CC_BEGIN //内存追踪宏 #if CC_USE_MEM_LEAK_DETECTION static void trackRef(Ref* ref); static void untrackRef(Ref* ref); #endif //构造函数,引用计数值初始化1 Ref::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; #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 != NULL && pEngine->getScriptType() == kScriptTypeJavascript) { pEngine->removeScriptObjectByObject(this); } } #endif //使用了内存泄露跟踪,如果引用计数不为0,清除 #if CC_USE_MEM_LEAK_DETECTION if (_referenceCount != 0) untrackRef(this); #endif } //调用了retain后,会使引用计数的值加1 void Ref::retain() { CCASSERT(_referenceCount > 0,"reference count should greater than 0"); ++_referenceCount; } //使用了release后,会把引用计数的值减1,当引用计数的值等于0时会删除该对象,所以当你如果使用了retain函数后,引用计数的值其实应该为2,然后在次调用release会进行引用计数值减1,但引用计数的值为1,所以该对象是不会被删除的。可以修改引用计数的值初始化为1,当调用release以后值就是为0,会被删除掉。这里发现一个问题,cocos只进行了delete this,并没有把this=nullptr,不知这样会不会出现野指针,以后验证一下,这里只是猜测一下。。 void Ref::release() { CCASSERT(_referenceCount > 0,"reference count should greater than 0"); --_referenceCount; if (_referenceCount == 0) { #if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0) ........................................... #endif #if CC_USE_MEM_LEAK_DETECTION untrackRef(this); #endif delete this; } } //当调用autorelease以后,该对象的内存管理就交给了cocos的内存池管理器,内存管理器具体内容,稍后在分析 Ref* Ref::autorelease() { PoolManager::getInstance()->getCurrentPool()->addObject(this); return this; } //显然这个函数是获得引用计数的值 unsigned int Ref::getReferenceCount() const { return _referenceCount; } //这是cocos内存泄露的检查,没使用过,具体不清楚,以后体验一下 #if CC_USE_MEM_LEAK_DETECTION static std::list<Ref*> __refAllocationList; void Ref::printLeaks() { // Dump Ref object memory leaks 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!"); // Create memory allocation record. __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_DETECTION NS_CC_END
接下了是cocos内存池管理器的实现
//首先是头文件
//CCAutoReleasePool.h
#ifndef __AUTORELEASEPOOL_H__ #define __AUTORELEASEPOOL_H__ #include <stack> #include <vector> #include <string> #include "base/CCRef.h" NS_CC_BEGIN /** * @addtogroup base_nodes * @{ */ class CC_DLL AutoreleasePool { public: /** * @warn Don't create an auto release pool in heap,create it in stack. * @js NA * @lua NA */ AutoreleasePool(); /** * Create an autorelease pool with specific name. This name is useful for debugging. */ AutoreleasePool(const std::string &name); /** * @js NA * @lua NA */ ~AutoreleasePool(); /** * Add a given object to this pool. * * The same object may be added several times to the same pool; When the * pool is destructed,the object's Ref::release() method will be called * for each time it was added. * * @param object The object to add to the pool. * @js NA * @lua NA */ void addObject(Ref *object); /** * Clear the autorelease pool. * * Ref::release() will be called for each time the managed object is * added to the pool. * @js NA * @lua NA */ void clear(); #if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0) /** * Whether the pool is doing `clear` operation. */ bool isClearing() const { return _isClearing; }; #endif /** * Checks whether the pool contains the specified object. */ bool contains(Ref* object) const; /** * Dump the objects that are put into autorelease pool. It is used for debugging. * * The result will look like: * Object pointer address object id reference count * */ void dump(); private: /** * The underlying array of object managed by the pool. * * Although Array retains the object once when an object is added,proper * Ref::release() is called outside the array to make sure that the pool * does not affect the managed object's reference count. So an object can * be destructed properly by calling Ref::release() even if the object * is in the pool. */ std::vector<Ref*> _managedObjectArray; std::string _name; #if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0) /** * The flag for checking whether the pool is doing `clear` operation. */ bool _isClearing; #endif }; class CC_DLL PoolManager { public: /** * @js NA * @lua NA */ CC_DEPRECATED_ATTRIBUTE static PoolManager* sharedPoolManager() { return getInstance(); } static PoolManager* getInstance(); /** * @js NA * @lua NA */ CC_DEPRECATED_ATTRIBUTE static void purgePoolManager() { destroyInstance(); } static void destroyInstance(); /** * Get current auto release pool,there is at least one auto release pool that created by engine. * You can create your own auto release pool at demand,which will be put into auto releae pool stack. */ AutoreleasePool *getCurrentPool() const; bool isObjectInPools(Ref* obj) const; /** * @js NA * @lua NA */ friend class AutoreleasePool; private: PoolManager(); ~PoolManager(); void push(AutoreleasePool *pool); void pop(); static PoolManager* s_singleInstance; std::deque<AutoreleasePool*> _releasePoolStack; AutoreleasePool *_curReleasePool; }; // end of base_nodes group /// @} NS_CC_END #endif //__AUTORELEASEPOOL_H__
//CCAutoReleasePool.cpp
#include "base/CCAutoreleasePool.h" #include "base/ccMacros.h" NS_CC_BEGIN
//AutoreleasePool的构造函数,在此处初始化一个vector的变量,空间为150,并把当前autoreleasePool的对象放入PoolManage维持的一个vector当中,AutoReleasePool的有两个构造函数,cocos会默认调用带参数的构造函数,后面会分析为什么会调用带参数的构造函数 AutoreleasePool::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); } //autoRelesae的析构函数,从PoolManager的Vector中弹出autorelease的对象 AutoreleasePool::~AutoreleasePool() { CCLOGINFO("deallocing AutoreleasePool: %p",this); clear(); PoolManager::getInstance()->pop(); } //把继承Ref的子节点放入AutoreleasePool的vector中 void AutoreleasePool::addObject(Ref* object) { _managedObjectArray.push_back(object); } //删除vector里面的对象 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 } //判断对象是否在autoreleasePool当中 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 %dn",_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%20un",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
//实例化了autoreleasePool对象,调用了但参数的构造函数,并放入poolmanager的vector当中,并赋值给poolmanager的curReleasePool s_singleInstance->_curReleasePool = new AutoreleasePool("cocos2d autorelease pool"); s_singleInstance->_releasePoolStack.push_back(s_singleInstance->_curReleasePool); } return s_singleInstance; } //PoolManager的清除 void PoolManager::destroyInstance() { delete s_singleInstance; s_singleInstance = nullptr; } PoolManager::PoolManager() { } //析构函数,清除vector PoolManager::~PoolManager() { CCLOGINFO("deallocing PoolManager: %p",this); while (!_releasePoolStack.empty()) { AutoreleasePool* pool = _releasePoolStack.back(); _releasePoolStack.pop_back(); delete pool; } } //返回当前的autoreleasePool的对象 AutoreleasePool* PoolManager::getCurrentPool() const { return _curReleasePool; } //判断是否在cocos的自动内存管理 bool PoolManager::isObjectInPools(Ref* obj) const { for (const auto& pool : _releasePoolStack) { if (pool->contains(obj)) return true; } return false; } //把autoReleasePOOL放入PoolManager当中 void PoolManager::push(AutoreleasePool *pool) { _releasePoolStack.push_back(pool); _curReleasePool = pool; } //把autoreleasePOOL从PoolManager当中删除 void PoolManager::pop() { // Can not pop the pool that created by engine CC_ASSERT(_releasePoolStack.size() >= 1); _releasePoolStack.pop_back(); // Should update _curReleasePool if a temple pool is released if (_releasePoolStack.size() > 1) { _curReleasePool = _releasePoolStack.back(); } } NS_CC_END
从以上的分析可知,cocos的内存管理是采用Poolmanager来管理,里面管理的是AutoReleasePool的对象,而继承与Ref的子节点都是AutoRelasePool来管理的,因为他们都放入了AutoReleasePool维持的一个verctor当中。即AutoReleasePool包含了继承Ref的子节点,而PoolManager则保存了AutoReleasePool的对象。
以上是个人的理解,有不当的地方,请大神指出,不胜感激!!!! (编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|