게임을 끝내고 나가면 가끔 죽었다. 원인을 찾아 고쳤는데 며칠 뒤 다른 자리에서 같은 것이 났다.
Table of contents
Open Table of contents
한 곳을 고쳤다
Cocos2d-x 의 onExit() 에서 적 노드의 동작을 멈추고 있었다. 그 시점에 노드가 이미 정리된 경우가 있었다.
CCNode.h 주석에도 적혀 있다. onExit 중에는 형제 노드에 접근할 수 없다는 것이다.
if (m_enemy) {
m_enemy->stopAllActions();
m_enemy = NULL;
}
있는지 확인하고 쓰게 하고 쓴 뒤에는 비워 뒀다. 이걸로 그 자리는 안 죽었다.
다른 자리에서 또 났다
며칠 뒤 보스전에서 같은 것이 났다. grep -rn "onExit" 으로 열어 보니 같은 모양이 확인 없이 그대로 있었다.
처음 고칠 때 이 자리를 안 봤다. onExit() 을 넣은 곳이 14 곳이었다. 하나씩 열어 보니 넷이 더 같은 모양이었다.
한 자리를 고쳤으면 같은 모양이 어디 또 있는지 세는 것이 다음 일이었다.
잊을 자리를 없앴다
찾아서 고치는 것으로는 다음에 또 생긴다. 새 화면을 만드는 사람이 그 확인을 안 넣으면 그만이기 때문이다.
CCLayer 를 상속한 공통 부모를 두고 onExit() 에서 하는 일을 거기에 모았다. 각 화면은 자기가 들고 있는 것만 적는다.
virtual void onExit() {
CCLayer::onExit();
releaseNodes();
}
확인하는 것 자체는 Cocos2d-x 에 이미 있었다. CCPlatformMacros.h 의 CC_SAFE_RELEASE_NULL 이 그대로 그 모양이다.
#define CC_SAFE_RELEASE_NULL(p) if(p) { p->release(); p = 0; }
손으로 쓰던 if 를 이걸로 바꾸니 빠뜨릴 자리가 없어졌다.
확인하는 코드에는 왜 확인하는지도 적었다. 이유가 없으면 필요 없어 보인다며 지우는 사람이 나온다. onExit() 은 CCScene 이 바뀔 때도 불리고 자식 노드가 먼저 정리된 뒤에도 불린다. 그것을 주석에 남겼다.
가끔 나는 것을 개발 중에 만나게
문제는 이게 가끔만 났다는 것이다. 개발 중에는 잘 안 나서 고쳤는지도 확인이 어려웠다.
CCAssert 를 넣었다. COCOS2D_DEBUG 가 켜진 빌드에서만 돌고, 이미 정리된 노드를 만지면 그 자리에서 멈춘다. 조용히 지나가면 나중에 다른 자리에서 죽고 원인이 안 보인다.
가끔 나는 것은 가끔 안 나는 것이 아니라 조건이 맞을 때만 나는 것이다. 그 조건을 개발 중에 만나게 하는 것이 방법이었다.
onExit() 에서 정리할 것이 더 있는지도 같이 봤다. 넷이었는데 둘만 하고 있었다.
CCScheduler unscheduleAllSelectorsForTarget(this)
CCTouchDispatcher removeDelegate(this)
콜백 등록 직접 지움
SimpleAudioEngine stopAllEffects()
removeDelegate 를 안 부르면 사라진 객체로 터치가 간다. 한 자리로 모은 김에 빠진 것도 넣었는데, 모으지 않았으면 각 파일에서 무엇이 빠졌는지 못 봤을 것이다.
정리
- 한 자리를 고쳤으면 같은 모양이 어디 또 있는지 센다
- 찾아서 고치는 것만으로는 다음에 또 생긴다
- 공통으로 하는 일을 위쪽에 두고 각자는 자기 것만 적게 한다
- 확인하는 코드에는 왜 확인하는지 적는다. 없으면 지워진다
- 손으로 쓰던
if는CC_SAFE_RELEASE_NULL이 이미 하고 있었다 - 가끔 나는 것은 조건이 맞을 때만 나는 것이다
- 그 조건을 개발 중에 만나게 하는
CCAssert를 둔다 - 조용히 지나가면 나중에 다른 자리에서 죽고 원인이 안 보인다
- 한 자리로 모으면 각 파일에서 무엇이 빠졌는지도 보인다