13.【cococos 2 d-xソース分析】:メモリ管理の詳細分析
67765 ワード
対応ソース位置:(1)cococos 2 d-x-3.3cococosbaseCCRef;(2)cocos2d-x-3.3\cocos\base\CCAutoreleasePool
エンジン内のメモリ管理の場所
これは
自動メモリ管理が必要なオブジェクトは
その実現も簡単だ.
その主な考え方は、ノードを例にとると、ノードオブジェクトが生成されると、その参照カウントは1になり、
彼は
最後に
次のオーディオシステムの実現を分析します.
エンジン内のメモリ管理の場所
void DisplayLinkDirector::mainLoop()
{
if (_purgeDirectorInNextLoop)
{
_purgeDirectorInNextLoop = false;
purgeDirector();
}
else if (! _invalid)
{
drawScene();
// release the objects
//
PoolManager::getInstance()->getCurrentPool()->clear();
}
}
これは
Ref
から自動メモリ管理が必要なオブジェクトは
Ref
クラスから継承されます.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;
};
その実現も簡単だ.
// 1
Ref::Ref()
: _referenceCount(1) // when the Ref is created, the reference count of it is 1
{
}
Ref::~Ref()
{
}
void Ref::retain()
{
// 1
CCASSERT(_referenceCount > 0, "reference count should greater than 0");
++_referenceCount;
}
void Ref::release()
{
CCASSERT(_referenceCount > 0, "reference count should greater than 0");
--_referenceCount;
// 0
if (_referenceCount == 0)
{
delete this;
}
}
Ref* Ref::autorelease()
{
//
PoolManager::getInstance()->getCurrentPool()->addObject(this);
return this;
}
//
unsigned int Ref::getReferenceCount() const
{
return _referenceCount;
}
AutoreleasePool
の実装その主な考え方は、ノードを例にとると、ノードオブジェクトが生成されると、その参照カウントは1になり、
AutoreleasePool
に自発的に追加されます.1つの場合、親ノードに追加されると、親ノードはカウントを1加算します.このフレームの最後に、AutoreleasePool
はクリーンアップを行い、そのオブジェクトのカウントを1に減算し、このときカウントは1になる.AutoreleasePool
クリーンアップ後、管理されているキューは空になります.彼はこのフレームにかかわらず、その後も無事だったため、親ノードが削除された後、そのカウントは0になったので、構造関数クリーンアップを呼び出します.もう1つの場合、親ノードが彼を収容せず、ずっとカウントを1にし、このフレームの最後のAutoreleasePool
までクリーンアップを行い、そのオブジェクトのカウントを1に減らし、このときカウントを0にするので、構造関数クリーンアップを呼び出し、合理的である.class CC_DLL AutoreleasePool
{
public:
AutoreleasePool();
//
AutoreleasePool(const std::string &name);
~AutoreleasePool();
//
void addObject(Ref *object);
//
void clear();
//
bool contains(Ref* object) const;
private:
// vector
std::vector<Ref*> _managedObjectArray;
//
std::string _name;
};
//
AutoreleasePool::AutoreleasePool()
: _name("")
{
_managedObjectArray.reserve(150);
PoolManager::getInstance()->push(this);
}
AutoreleasePool::AutoreleasePool(const std::string &name)
: _name(name)
{
// 150
_managedObjectArray.reserve(150);
// PoolManager
PoolManager::getInstance()->push(this);
}
AutoreleasePool::~AutoreleasePool()
{
//
clear();
PoolManager::getInstance()->pop();
}
void AutoreleasePool::addObject(Ref* object)
{
_managedObjectArray.push_back(object);
}
void AutoreleasePool::clear()
{
// release
for (const auto &obj : _managedObjectArray)
{
obj->release();
}
// vector
_managedObjectArray.clear();
}
//
bool AutoreleasePool::contains(Ref* object) const
{
//
for (const auto& obj : _managedObjectArray)
{
if (obj == object)
return true;
}
return false;
}
PoolManager
の実装AutoreleasePool
自体は、各フレームの最後に使用するためであり、すなわち、管理された参照カウントが1オブジェクトであるクリーンアップが必要なオブジェクトは、このフレームの最後にクリーンアップされるが、クリーンアップが必要なオブジェクトが大量に生成されるフレームもあるため、場合によっては、ユーザがカスタマイズしたAutoreleasePool
を実装してクリーンアップサイクルを短縮する必要がある.これがPoolManager
の役割です.{
// PoolManager
//
new AutoreleasePool();
auto p1=new Node();
p1->autorelease();
auto p2=new Node();
p2->autorelease();
auto p3=new Node();
p3->autorelease();
//
//
//
}
PoolManager
を見てclass CC_DLL PoolManager
{
public:
static PoolManager* getInstance();
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;
// AutoreleasePool
std::vector<AutoreleasePool*> _releasePoolStack;
};
//
PoolManager* PoolManager::s_singleInstance = nullptr;
PoolManager* PoolManager::getInstance()
{
if (s_singleInstance == nullptr)
{
s_singleInstance = new (std::nothrow) PoolManager();
// Add the first auto release pool
// PoolManager 。
new AutoreleasePool("cocos2d autorelease pool");
}
return s_singleInstance;
}
void PoolManager::destroyInstance()
{
delete s_singleInstance;
s_singleInstance = nullptr;
}
PoolManager::PoolManager()
{
_releasePoolStack.reserve(10);
}
PoolManager::~PoolManager()
{
//
while (!_releasePoolStack.empty())
{
AutoreleasePool* pool = _releasePoolStack.back();
delete pool;
}
}
AutoreleasePool* PoolManager::getCurrentPool() const
{
// pool
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()
{
//
_releasePoolStack.pop_back();
}
RefPtr
の実装彼は
share_ptr
に倣って実現した.1つの要素の場合、メモリを自動的に管理するには、release
とretain
を手動で行うのは不便です.したがって,局所変数の構造/構造関数にパッケージ化し,カウント管理を容易にする.// do{}while(0); https://www.jianshu.com/p/99efda8dfec9
#define CC_REF_PTR_SAFE_RETAIN(ptr)\
\
do\
{\
if (ptr)\
{\
const_cast(static_cast(ptr))->retain();\
}\
\
} while (0);
#define CC_REF_PTR_SAFE_RELEASE(ptr)\
\
do\
{\
if (ptr)\
{\
const_cast(static_cast(ptr))->release();\
}\
\
} while (0);
#define CC_REF_PTR_SAFE_RELEASE_NULL(ptr)\
\
do\
{\
if (ptr)\
{\
const_cast(static_cast(ptr))->release();\
ptr = nullptr;\
}\
\
} while (0);
//
template <typename T> class RefPtr
{
public:
inline RefPtr()
:
_ptr(nullptr)
{
}
//
inline RefPtr(RefPtr<T> && other)
{
_ptr = other._ptr;
other._ptr = nullptr;
}
inline RefPtr(T * ptr)
:
_ptr(const_cast<typename std::remove_const<T>::type*>(ptr)) // Const cast allows RefPtr to reference objects marked const too.
{
CC_REF_PTR_SAFE_RETAIN(_ptr);
}
inline RefPtr(std::nullptr_t ptr)
:
_ptr(nullptr)
{
}
inline RefPtr(const RefPtr<T> & other)
:
_ptr(other._ptr)
{
CC_REF_PTR_SAFE_RETAIN(_ptr);
}
inline ~RefPtr()
{
CC_REF_PTR_SAFE_RELEASE_NULL(_ptr);
}
inline RefPtr<T> & operator = (const RefPtr<T> & other)
{
if (other._ptr != _ptr)
{
CC_REF_PTR_SAFE_RETAIN(other._ptr);
CC_REF_PTR_SAFE_RELEASE(_ptr);
_ptr = other._ptr;
}
return *this;
}
inline RefPtr<T> & operator = (RefPtr<T> && other)
{
if (&other != this)
{
CC_REF_PTR_SAFE_RELEASE(_ptr);
_ptr = other._ptr;
other._ptr = nullptr;
}
return *this;
}
inline RefPtr<T> & operator = (T * other)
{
if (other != _ptr)
{
CC_REF_PTR_SAFE_RETAIN(other);
CC_REF_PTR_SAFE_RELEASE(_ptr);
_ptr = const_cast<typename std::remove_const<T>::type*>(other); // Const cast allows RefPtr to reference objects marked const too.
}
return *this;
}
inline RefPtr<T> & operator = (std::nullptr_t other)
{
CC_REF_PTR_SAFE_RELEASE_NULL(_ptr);
return *this;
}
// Note: using reinterpret_cast<> instead of static_cast<> here because it doesn't require type info.
// Since we verify the correct type cast at compile time on construction/assign we don't need to know the type info
// here. Not needing the type info here enables us to use these operations in inline functions in header files when
// the type pointed to by this class is only forward referenced.
inline operator T * () const { return reinterpret_cast<T*>(_ptr); }
inline T & operator * () const
{
CCASSERT(_ptr, "Attempt to dereference a null pointer!");
return reinterpret_cast<T&>(*_ptr);
}
inline T * operator->() const
{
// reinterpret_cast
return reinterpret_cast<T*>(_ptr);
}
inline T * get() const { return reinterpret_cast<T*>(_ptr); }
inline bool operator == (const RefPtr<T> & other) const { return _ptr == other._ptr; }
inline bool operator == (const T * other) const { return _ptr == other; }
inline bool operator == (typename std::remove_const<T>::type * other) const { return _ptr == other; }
inline bool operator == (const std::nullptr_t other) const { return _ptr == other; }
inline bool operator != (const RefPtr<T> & other) const { return _ptr != other._ptr; }
inline bool operator != (const T * other) const { return _ptr != other; }
inline bool operator != (typename std::remove_const<T>::type * other) const { return _ptr != other; }
inline bool operator != (const std::nullptr_t other) const { return _ptr != other; }
inline bool operator > (const RefPtr<T> & other) const { return _ptr > other._ptr; }
inline bool operator > (const T * other) const { return _ptr > other; }
inline bool operator > (typename std::remove_const<T>::type * other) const { return _ptr > other; }
inline bool operator > (const std::nullptr_t other) const { return _ptr > other; }
inline bool operator < (const RefPtr<T> & other) const { return _ptr < other._ptr; }
inline bool operator < (const T * other) const { return _ptr < other; }
inline bool operator < (typename std::remove_const<T>::type * other) const { return _ptr < other; }
inline bool operator < (const std::nullptr_t other) const { return _ptr < other; }
inline bool operator >= (const RefPtr<T> & other) const { return _ptr >= other._ptr; }
inline bool operator >= (const T * other) const { return _ptr >= other; }
inline bool operator >= (typename std::remove_const<T>::type * other) const { return _ptr >= other; }
inline bool operator >= (const std::nullptr_t other) const { return _ptr >= other; }
inline bool operator <= (const RefPtr<T> & other) const { return _ptr <= other._ptr; }
inline bool operator <= (const T * other) const { return _ptr <= other; }
inline bool operator <= (typename std::remove_const<T>::type * other) const { return _ptr <= other; }
inline bool operator <= (const std::nullptr_t other) const { return _ptr <= other; }
inline operator bool() const { return _ptr != nullptr; }
inline void reset()
{
CC_REF_PTR_SAFE_RELEASE_NULL(_ptr);
}
inline void swap(RefPtr<T> & other)
{
if (&other != this)
{
Ref * tmp = _ptr;
_ptr = other._ptr;
other._ptr = tmp;
}
}
//
inline void weakAssign(const RefPtr<T> & other)
{
CC_REF_PTR_SAFE_RELEASE(_ptr);
_ptr = other._ptr;
}
private:
Ref * _ptr;
};
最後に
次のオーディオシステムの実現を分析します.