[기초]Cocos2d-x 메모리 관리 메커니즘

【성명】
본 논문 의 저작권 은 젤리 가 소유 하고 싶 습 니 다.전 재 를 환영 합 니 다.그러나 작가 의 동의 없 이 이 성명 을 보류 하고 글 페이지 의 뚜렷 한 위치 에서 원문 링크 를 제시 해 야 합 니 다.그렇지 않 으 면 법률 적 책임 을 추궁 할 권 리 를 보류 합 니 다.만약 이 문장 이 너 에 게 도움 이 된다 면,너 는 할 수 있다.
커피 한잔 주세요.
» 
본문 링크:http://www.jellythink.com/archives/776
» 
본 사이트 구독:http://www.jellythink.com/feed
» 
젤리 » 원본 링크:http://www.jellythink.com/archives/776
[본문]
영원히 변 하지 않 는 물건
지금까지 메모리 가 매우 싸 지만 무한대 로 사용 할 수 있 는 것 도 아 닙 니 다.특히 모 바 일 에서 그 정도 의 메모리,그 많은 앱 을 앞 다 투어 사용 해 야 합 니 다.잘 모 르 겠 습 니 다.당신 이 차지 하 는 메모리 가 너무 많 습 니 다.시스템 은 당신 의 앱 을 직접 처리 합 니 다.그래서 우 리 는 또 자주 이야기 해 야 합 니 다.메모리 관리.COM 개발 을 정리 할 때 COM 의 메모리 관리 모델 을 분석 한 적 이 있다.루 아 를 정리 할 때 루 아의 메모리 회수 메커니즘 도 분석 했다.며칠 전 에는 C++의 스마트 포인터 가 메모리 사용 에 대한 응용 도 전문 적 으로 썼 다.이 를 통 해 알 수 있 듯 이 메모리 관 리 는 언어 차원 이 든 라 이브 러 리 차원 이 든 모두 엄격 한 기준 과 실 시 를 가진다.Cocos2d-x 에 있어 서도 마찬가지다.그렇다면 Cocos2d-x 에 서 는 메모리 관 리 를 어떻게 합 니까?이 글 은 Cocos2d-x 의 메모리 관리 에 관 한 지식 을 정리 하 겠 습 니 다.면접 관 의 다섯 손가락 관문 을 가볍게 넘 길 수 있 게 해 드 립 니 다.
Cocos2d-x 메모리 관리
메모리 관리 라 는 추상 적 인 것 을 탐구 하 는 데 가장 간단 한 방법 은 코드 를 통 해 연구 하 는 것 이다.먼저 간단 한 장면 을 만들어 서 Cocos2d-x 가 대상 을 만 들 때 무엇 을 했 는 지 살 펴 보 자.
Scene 만 들 기:
auto scene = Scene::create();

함수.create
정적 함수 입 니 다.보 세 요.create
함수 의 원본 코드:
Scene *Scene::create()
{
    Scene *ret = new Scene();
    if (ret && ret->init())
    {
        ret->autorelease();
        return ret;
    }
    else
    {
        CC_SAFE_DELETE(ret);
        return nullptr;
    }
}

이 제 는 Cocos2d-x 의 메모리 관리 에 관 한 지식 이 언급 되 었 다.Cocos2d-x 에서 대상 의 생 성과 초기 화 는 모두 사용 하 는 new 와init함수 의 조합 방식 입 니 다.이런 방식 을 2 단 식 생 성 이 라 고 합 니 다.C++에서 구조 함수 가 반환 값 이 없 기 때문에 구조 함 수 를 통 해 초기 화의 성공 과 실 패 를 확정 할 수 없습니다.그래서 Cocos2d-x 에서 이런 2 단 식 생 성 방식 을 크게 사 용 했 습 니 다.사용 하기에 괜 찮 습 니 다.앞으로 자신의 프로젝트 에서 도 채용 할 수 있다.
이런 방식 은 Cocos2d-x 에서 자주 사용 되 기 때문에 그 녀석 들 을 터치 하여 매크로 를 만 들 었 다.CREATE_FUNC우리 클래스 도 이러한 2 단 식 으로 만 드 는 방식 을 사용 하려 면 우리 클래스 에 다음 과 같은 코드 를 추가 해 야 합 니 다.
CREATE_FUNC(classname);

동시에 init 함 수 를 정의 해 야 합 니 다.그러면 OK 입 니 다.우리 이 매크로 를 보 자.
#define CREATE_FUNC(__TYPE__) \
static __TYPE__* create() \
{ \
    __TYPE__ *pRet = new __TYPE__(); \
    if (pRet && pRet->init()) \
    { \
        pRet->autorelease(); \
        return pRet; \
    } \
    else \
    { \
        delete pRet; \
        pRet = NULL; \
        return NULL; \
    } \
}

그런데 이 물건 들 도 모두 기본 적 인 C++지식 입 니 다.할 말 이 별로 없습니다.코드 에 있 는ret->autorelease()을 보면 망연자실 합 니 다.네,Cocos2d-x 의 메모리 관리 촉각 을 보 았 습 니 다.ret->autorelease()뭐 예요?내 가 create 함 수 를 사용 하여 장면 을 만 든 후에 나 는 delete 에 가지 않 았 다.이것 도 문제 가 없다.문 제 는 바로 이autorelease의 사용 에서 발생 했다.서막 이 끝 났 으 니 진정 으로 Cocos2d-x 의 메모리 관 리 를 시작 합 시다.
Cocos2d-x 에서 메모리 관리 에 관 한 종 류 는 다음 과 같 습 니 다.
Ref 클래스;
AutoreleasePool 클래스;
PoolManager 클래스.
Ref 류 는 거의 Cocos2d-x 의 모든 종류의 부류 로 Cocos2d-x 에서 메모리 관리의 가장 중요 한 일환 이다.위 에서 말 한autorelease함 수 는 Ref 류 의 구성원 함수 에 대해 Cocos2d-x 에서 Ref 류 를 계승 하 는 모든 클래스 는 Cocos2d-x 의 메모리 관 리 를 사용 할 수 있다.
AutoreleasePool 클래스 는 자동 방출 대상 을 관리 하 는 데 사 용 됩 니 다.
PoolManager 는 모든 AutoreleasePool 을 관리 하 는 데 사 용 됩 니 다.이 종 류 는 단일 모드 로 이 루어 집 니 다.
다음은 상기 세 가지 유형의 소스 코드 를 분석 함으로써 Cocos2d-x 가 어떻게 메모리 관 리 를 하 는 지 살 펴 보 겠 습 니 다.
Ref 류
먼저 Ref 류 의 정 의 를 살 펴 보 겠 습 니 다.다음은 Ref 류 의 헤더 파일 정의 입 니 다.
class CC_DLL Ref
{
public:
    /**
    *         
    *          
    */
    void retain();

    /**
    *           
    *             ,       0 ,        
    */
    void release();

    /**
    *           
    *            
    *         ,             ,              
    *     release  ,         0 ,       。
    */
    Ref* autorelease();

    /**
    *            
    *         ,     1
    */
    unsigned int getReferenceCount() const;
};

...에 대하 여release
함수 의 실현 은 여기 서 특별히 정리 해 야 합 니 다.먼저 그것 의 실현 을 보 세 요.
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))
        {
            //           ,     Cocos2d-x        
            //       0,          autorelease     ,          
            //     ,          0 ,        ,     autorelease  ,
            //   autorelease        ,      ,        Cocos2d-x     
            //           。
            // 
            //            new/retain autorelease/release         
            CCASSERT(false, "The reference shouldn't be 0 because it is still in autorelease pool.");
        }
#endif
        delete this;
    }
}

위 에서 도 말 했 듯 이 new 와 autorelease 는 일치 하 게 사용 해 야 합 니 다.retain 과 release 도 일치 하 게 사용 해 야 합 니 다.그렇지 않 으 면 단언 오류 나 메모리 유출 이 발생 할 수 있 습 니 다.비 Debug 모드 에서 바로 물 러 날 수 있 습 니 다.이것 이 바로 우리 가 create 함 수 를 사용 할 때 new 가 성공 한 후에 autorelease 를 호출 하여 이 대상 을 자동 방출 탱크 에 넣 는 이유 입 니 다.그리고 우리 가 다시 이 대상 을 얻 고 이 대상 을 사용 하려 면 retain 을 사용 하여 이 대상 의 소유권 을 다시 얻어 야 합 니 다.물론 사용 이 끝 난 후에 release 를 호출 하여 수 동 으로 석방 작업 을 완성 하 는 것 을 기억 해 야 합 니 다.이것 은 당신 의 임무 입 니 다.예 를 들 어 다음 코드:
auto obj = Scene::create();
obj->autorelease(); // Error

이것 은 잘못된 것 입 니 다.create 에서 생 성 에 성공 한 상태 에서 obj 대상 을 autorelease pool 에 넣 었 습 니 다.autorelease pool 을 다시 넣 으 면 autorelease pool 을 없 애 면 한 대상 을 두 번 이나 없 애 는 경우 가 발생 하고 프로그램의 crash 가 발생 합 니 다.예 를 들 어 다음 코드 도 잘못 되 었 습 니 다.
auto obj = Scene::create();
obj->release(); // Error

create 함 수 를 사용 하여 대상 을 만 든 후,obj 는 소유권 이 없 으 며,release 를 다시 호출 하면 잘못된 대상 이 방출 됩 니 다.정확 한 방법 은 다음 과 같다.
auto obj = Scene::create(); //   retain release  ,release     autorelease    (    create       )   retain
obj->retain();
obj->release();

이 인용 수 는 COM 의 AddRef 와 Release 를 떠 올 리 게 한다.
AutoreleasePool 클래스
Autorelease Pool 류 는 Ref 류 의 유원 류 입 니 다.먼저 Autorelease 류 의 성명 을 보십시오.
class CC_DLL AutoreleasePool
{
public:
    /**
    *        AutoreleasePool  ,       
    *       ,         ,AutoreleasePool         ,  RAII     
    */
    AutoreleasePool();

    /**
    *            autorelease pool  
    *       ,          。
    */
    AutoreleasePool(const std::string &name);

    ~AutoreleasePool();

    /**
    *  autorelease pool     ref  
    *                     (         )
    *             ,               release()  
    */
    void addObject(Ref *object);

    /**
    *        
    *              release()  
    */
    void clear();

#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
    /**
    *                     
    */
    bool isClearing() const { return _isClearing; };
#endif

    /**
    *               Ref  
    */
    bool contains(Ref* object) const;

    /**
    *   autorelease pool      
    */
    void dump();

private:
    /**
    *           std::vector    
    */
    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
};

AutoreleasePool 류 에 있어 서 그 실현 은 매우 간단 합 니 다.대상 을 std::vector 에 간단하게 저장 하 는 것 입 니 다.이 AutoreleasePool 을 풀 때 std::vector 에 저 장 된 대상 에 게 해당 하 는 release 함 수 를 순서대로 호출 하여 대상 의 자동 방출 을 완성 하 는 것 입 니 다.
PoolManager 클래스
이 물건 은 또 무엇 을 하 는 것 입 니까?우리 가 AutoreleasePool 의 소스 코드 를 읽 을 때 구조 함수 에서 다음 과 같은 코드 를 발견 할 수 있 습 니 다.
AutoreleasePool::AutoreleasePool()
: _name("")
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
, _isClearing(false)
#endif
{
    _managedObjectArray.reserve(150);
    PoolManager::getInstance()->push(this);
}

AutoreleasePool 의 분석 함수 에는 다음 과 같은 코드 가 있 습 니 다.
AutoreleasePool::~AutoreleasePool()
{
    CCLOGINFO("deallocing AutoreleasePool: %p", this);
    clear();

    PoolManager::getInstance()->pop();
}

아,원래 우 리 는 AutoreleasePool 대상 을 PoolManager 에 다시 넣 었 군요.원래 PoolManager 클래스 는 모든 AutoreleasePool 클래스 를 관리 하 는 데 사용 되 고 하나의 예 모드 로 이 루어 집 니 다.이 PoolManger 는 AutoreleasePool 대상 지침 을 저장 하 는 stack 이 있 습 니 다.이 stack 은 std:vector 에 의 해 이 루어 집 니 다.주의해 야 할 것 은 cocos2d-x 의 단일 사례 류 는 모두 스 레 드 가 안전 하지 않 고 메모리 관리 와 밀접 한 관 계 를 가 진 PoolManager 류 도 예외 가 아니 므 로 다 중 스 레 드 에서 cocos2d-x 의 인 터 페 이 스 를 사용 할 때 메모리 관리 문 제 를 특히 주의해 야 한다.더욱 안전 한 단일 모델 에 대해 관심 이 있 는 학생 들 은 이 를 읽 을 수 있다.
》。다음은 PoolManager 의 헤더 파일 정 의 를 살 펴 보 겠 습 니 다.
class CC_DLL PoolManager
{
public:
    /**
    *     
    */
    static PoolManager* getInstance();

    /**
    *     
    */
    static void destroyInstance();

    /**
    *      autorelease pool,    ,      autorelease pool
    *       ,           release pool,     autorelease pool   PoolManager 
    */
    AutoreleasePool *getCurrentPool() const;

    /**
    *                autorelease pool 
    */
    bool isObjectInPools(Ref* obj) const;

    friend class AutoreleasePool;

private:
    PoolManager();
    ~PoolManager();

    /**
    *  AutoreleasePool     PoolManager 
    */
    void push(AutoreleasePool *pool);

    /**
    *  PoolManager   AutoreleasePool  
    */
    void pop();

    static PoolManager* s_singleInstance;

    /**
    *        AutoreleasePool  
    */
    std::deque<AutoreleasePool*> _releasePoolStack;
    AutoreleasePool *_curReleasePool;
};

PoolManager 의 각 함수 의 실현 에 대해 서도 매우 간단 합 니 다.여 기 는 피로 한 설명 을 하지 않 습 니 다.여러분 은 Cocos2d-x 의 소스 코드 를 읽 을 수 있 습 니 다.
문제 가 생 겼 다
이렇게 많은 말 을 했 고 코드 도 이렇게 많이 열 거 했 습 니 다.우 리 는 하나의 대상 을 만 든 후에 AutoreleasePool 에 넣 었 습 니 다.마지막 으로 AutoreleasePool 의 clear 함 수 를 호출 할 때 AutoreleasePool 이 관리 하 는 모든 대상 에 게 차례대로 release 작업 을 호출 합 니 다.아하!뭔 가 잘못된 것 같 습 니 다.저 는 최종 적 으로 누가 이 clear 함 수 를 호출 할 것 이 라 고 말 하지 않 았 습 니 다.예.감독 류 에 있 는 이 코드 를 보면 알 것 같 습 니 다.
void DisplayLinkDirector::mainLoop()
{
    if (_purgeDirectorInNextLoop)
    {
        _purgeDirectorInNextLoop = false;
        purgeDirector();
    }
    else if (! _invalid)
    {
        drawScene();

        // release the objects
        PoolManager::getInstance()->getCurrentPool()->clear();
    }
}

위의 코드 가 설명 한 사실은 이미지 렌 더 링 의 주 순환 에서 현재 그래 픽 대상 이 현재 프레임 에 있다 면 디 스 플레이 함 수 를 호출 하고 Autorelease Pool:clear()를 호출 하여 이 대상 들 의 인용 수 를 줄 이 는 것 입 니 다.mainLoop 은 모든 프레임 이 자동 으로 호출 되 기 때문에 다음 프레임 에 서 는 이 대상 들 이 현재 AutoreleasePool 대상 에 의 해 한 번 씩 릴 리 스 되 었 습 니 다.이것 도 Autorelease Pool'자동'의 이유 이다.
총결산
자,총 결 된 차이 가 많 지 않 습 니 다.Cocos2d-x 의 메모리 관리 에 대한 총 결 된 차이 가 많 지 않 습 니 다.Cocos2d-x 의 메모리 관리 에 대해 저 는 개인 적 으로 이 대상 의 인용 계수,retain 과 release,new 와 autorelease 가 일치 하 게 사용 되 어야 하 며 불필요 한 오류 가 발생 하지 않도록 해 야 한다 고 생각 합 니 다.이렇게 많아
종이 위 에서 얻 은 것 은 결국 얕 게 느껴 져 서,이 일 을 몸소 해 야 한 다 는 것 을 절대 알 지 못 한다.
실제 사용 을 거 쳐 코드 의 세련 을 거 쳐 야만 이런 것들 을 더욱 잘 파악 할 수 있다.Cocos2d-x 에서 많은 부분 에서 autorelease 나 retain 이 진행 되 었 습 니 다.우 리 는 다시 이런 조작 을 할 필요 가 없습니다.예 를 들 어 create,예 를 들 어 addChild 방법 으로 하위 노드 를 추가 할 때 자동 으로 retain 을 호출 합 니 다.대응 하 는 removeChild 를 통 해 하위 노드 를 제거 할 때 자동 으로 release 를 호출 합 니 다.이런 곳 들 은 조금 만 주의 하지 않 으 면 너 를 구덩이 에 빠 뜨 릴 수 있다.열심히 해,얘 들 아.
【부록】
원본 주소:http://www.jellythink.com/archives/776

좋은 웹페이지 즐겨찾기