cocos2dx가 보기에 아름다운 인용계수로 인한 메모리 유출 (1) CCCallFunc 대상

4367 단어
모든 코드는 무관한 부분을 차단하고 문제에 실질적인 영향을 미치는 부분만 보여준다.
인용 수는 다언할 필요가 없다. 다음은 RC라고 약칭한다.
RC의 두 가지 기본 원칙을 먼저 말씀드리겠습니다.
1. new와 delete 대상을 직접 사용하지 않고 RC를 통해 실현되며 RC는 0이고 대상은 소각됩니다.cocos2dx에서retain,release,autoRelease를 통해 실현됩니다.
2. 대상을 사용하려면 먼저retain,release를 다 써야 한다. 왜냐하면 이렇게 하지 않으면 사용하고 있는 물건이 누구에게 방출되었는지 알 수 없기 때문이다.특히 autoRelease가 있습니다.
만약 모두가 이 두 가지 원칙을 엄격히 준수한다면 메모리는 유출되지 않을 것이다.
나는 이전에도 이렇게 생각했지만, 나중에 나는 이것이 단지 보기에 매우 아름다울 뿐이라는 것을 발견하였다.
android의'독해'를 많이 입은 저는 최근에 프로젝트에서 이런 문제에 부딪혔습니다. 메모리의 점용은 줄어들지 않고 몇 장면을 더 전환하면 메모리 문제로 인해 시스템에 의해 죽습니다.단점 분노는 현재 장면에서 분석구를 사용하지 않은 것을 발견했다.소각해야 할 장면이 소각되지 않아 메모리 유출이 일어났다는 것이다.
왜냐하면 내가 button 컨트롤을 사용자 정의했기 때문이다. 다음과 같다.
class LCButton : public CCControl
{
public:
	void init(const char* normalPath  , const char* highlightPath , const char* disablePath , CCCallFunc *active , CCCallFunc *disactice);
	static LCButton* create(const char* normalPath  , const char* highlightPath , const char* disablePath , CCCallFunc *active , CCCallFunc *disactice , int priority = -127);

	void setDownSelector(CCCallFunc *call);
	void setUpSelector(CCCallFunc *call);
private:
	CCCallFunc *beginSelector;
	CCCallFunc *endSelector;
};

그 중에서 클릭 이벤트의 리셋에 대해 저는 CCCallfunc와 그 파의 아들 클래스로 실현했습니다. 왜냐하면 저는 CCCallfunc라는 클래스가 존재하는 의미가 바로 리셋을 하는 것이라고 생각하기 때문입니다.그렇다면 위에서 언급한 RC 원칙에 따라 나는 이렇게 써야 한다.
void LCButton::setDownSelector(CCCallFunc *call)
{
	if(beginSelector)
	{
		beginSelector->release();
	}
	beginSelector = call ;
	CC_SAFE_RETAIN(beginSelector);
}

이 대상의 존재 생명 주기 내에 이 리셋은 모두 작용해야 한다. 메모리를 방출하는 작업은 분석에 놓아야 한다. 그러면 나는 분석에서beginSelector를 방출해야 한다.
LCButton::~LCButton(void)
{
	CC_SAFE_RELEASE(beginSelector);
}

그리고 외부에서 이 구성 요소의 클래스를 사용합니다. 저도 이렇게 처리했습니다.create가 나온 후에retain, 분석 구조release가 메모리를 방출합니다.
문제는 다음과 같습니다. CCCallfunc의 create 함수를 살펴보십시오.
CCCallFunc * CCCallFunc::create(CCObject* pSelectorTarget, SEL_CallFunc selector) 
{
    CCCallFunc *pRet = new CCCallFunc();
    if (pRet && pRet->initWithTarget(pSelectorTarget)) { }
}
bool CCCallFunc::initWithTarget(CCObject* pSelectorTarget) {
    if (pSelectorTarget) 
    {
        pSelectorTarget->retain();
    }

    if (m_pSelectorTarget) 
    {
        m_pSelectorTarget->release();
    }
}

전송된this 바늘에 대해retain을 한 번 했습니다.
그러면 이callfunc 대상은 외부 장면this에 대한 인용을 저장하고,callfunc 대상이 방출되면this의 인용을 방출합니다.그러나callfunc는 나의button 컨트롤러에 의해 보유되고,button은callfunc를 방출하지만,button은this에 의해 보유되고,this는button을 방출한다.
정리해 보면this->button->callfunc->this와 같은 순환 인용 관계입니다.사라진 자물쇠 하나에 해당해서 모두가 풀 수 없다.
이 문제를 해결하려면 가장 간단한 것은child를 가진release를 앞당기는 것이다. 예를 들어onExit에 미리 가서 하는 것이다.그러나 이것은 디자인 이념과 어긋난다. 메모리를 방출하는 것은 분석해서 해야 할 일이기 때문이다.onExit는 청소만 합니다.cocos2dx 원본의 분석과 onExit 함수를 보십시오:
CCNode::~CCNode(void)
{
    CC_SAFE_RELEASE(m_pActionManager);
    CC_SAFE_RELEASE(m_pScheduler);
    CC_SAFE_RELEASE(m_pCamera);
    CC_SAFE_RELEASE(m_pGrid);
    CC_SAFE_RELEASE(m_pShaderProgram);
    CC_SAFE_RELEASE(m_pUserObject);
    CC_SAFE_RELEASE(m_pChildren);
    m_pComponentContainer->removeAll();
    CC_SAFE_DELETE(m_pComponentContainer);
}
void CCNode::onExit()
{
    this->pauseSchedulerAndActions();
    m_bRunning = false;
    arrayMakeObjectsPerformSelector(m_pChildren, onExit, CCNode*);    
}

또한 onExit에서 청소를 하고 메모리 방출을 분석한다.
그럼 또 하나는 CCCallfunc에서 리턴을 하지 않고 CCMenu에 있는create를 보는 것이다.
CCMenuItem* CCMenuItem::create(CCObject *rec, SEL_MenuHandler selector)
{
    CCMenuItem *pRet = new CCMenuItem();
    pRet->initWithTarget(rec, selector);
    pRet->autorelease();
    return pRet;
}
bool CCMenuItem::initWithTarget(CCObject *rec, SEL_MenuHandler selector)
{
    setAnchorPoint(ccp(0.5f, 0.5f));
    m_pListener = rec;
    m_pfnSelector = selector;
    m_bEnabled = true;
    m_bSelected = false;
    return true;
}

안에 this에 대한 리턴 처리가 없습니다.그러나 봉인된 리셋류가 있어서 사용하지 않고 단독으로 바퀴를 만드는 행위를 반복하는 것 같다.
CCCallfunc를 보면this의retain이 어떤 경우에도 필요합니다.예를 들어 전체적인 schedule 스케줄링을 할 때this가 풀려나면 아무것도 없다.
마지막으로 가장 실현하기 쉬운 첫 번째 방법을 선택하여 onExit로 앞당겼다.
더 좋은 방법이 있습니다.

좋은 웹페이지 즐겨찾기