13.【cococos 2 d-xソース分析】:メモリ管理の詳細分析


対応ソース位置:(1)cococos 2 d-x-3.3cococosbaseCCRef;(2)cocos2d-x-3.3\cocos\base\CCAutoreleasePool
エンジン内のメモリ管理の場所
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つの要素の場合、メモリを自動的に管理するには、releaseretainを手動で行うのは不便です.したがって,局所変数の構造/構造関数にパッケージ化し,カウント管理を容易にする.
//   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;
};

最後に
次のオーディオシステムの実現を分析します.