cocos2d-x 3.0 이후부터 기본으로 탑재되는 json 파서인 rapidjson

파일을 읽고 파싱을 하고 프로그램을 종료했더니

메모리 릭이 좌르르륵

알고봤더니 끝날때 Document를 RemoveAllMember()을 해줘야 하고 원래 filestring은 파싱하고 지워도 됨.


1
2
3
4
5
unsigned long bufferSize = 0;
const char* fileName = FileUtils::fullPathFromRelativePath("data/StringData.json");
unsigned char* generateFile = FileUtils::getFileData(fileName, "rb", &bufferSize);
 
m_document.Parse<kParseStopWhenDoneFlag>((const char*)generateFile, bufferSize);
cs

파일을 읽은 후

1
2
delete generateFile;
generateFile = NULL;
cs

파일 string은 delete

1
m_document.RemoveAllMembers();
cs

를 반드시 해줘야 메모리 릭이 생기지 않음

cJSON 쓸때도 CRT 메모리 릭이 있었는데 Visual Leak Detector 는 없었지만

근데 이렇게 다 지워주면 둘다 메모리 릭이 나지 않음


평소랑 다름없이 CCFileUtils를 통해 파일을 읽었는데

한글이 섞이면 제대로 읽어오지 않는 문제가 발생

알고보니 CCFileUtils는 UTF-8을 멀티바이트로 읽어오지 못함

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include "FileUtils.h"
 
unsigned char* FileUtils::getFileData(const char* pszFileName, const char* pszMode, unsigned long * pSize)
{
    unsigned char * pBuffer = NULL;
 
    do
    {
        wchar_t* pWszFileName = NULL;
        int pWszFileNameSize = MultiByteToWideChar(CP_ACP, 0, pszFileName, -1NULLNULL);
        pWszFileName = new wchar_t[pWszFileNameSize];
        MultiByteToWideChar(CP_ACP, 0, pszFileName, strlen(pszFileName) + 1, pWszFileName, pWszFileNameSize);
 
        wchar_t* pWszMode = NULL;
        int pWszModeSize = MultiByteToWideChar(CP_ACP, 0, pszMode, -1NULLNULL);
        pWszMode = new wchar_t[pWszModeSize];
        MultiByteToWideChar(CP_ACP, 0, pszMode, strlen(pszMode) + 1, pWszMode, pWszModeSize);
 
        // read the file from hardware
        FILE *fp = _wfopen(pWszFileName, pWszMode);
        CC_BREAK_IF(!fp);
 
        fseek(fp, 0, SEEK_END);
        *pSize = ftell(fp);
        fseek(fp, 0, SEEK_SET);
        pBuffer = new unsigned char[*pSize];
        *pSize = fread(pBuffer, sizeof(unsigned char), *pSize, fp);
        fclose(fp);
 
        delete pWszFileName;
        pWszFileName = NULL;
        delete pWszMode;
        pWszMode = NULL;
    } while (0);
 
    if (!pBuffer && getIsPopupNotify())
    {
        std::string title = "Notification";
        std::string msg = "Get data from file(";
        msg.append(pszFileName).append(") failed!");
 
        CCMessageBox(msg.c_str(), title.c_str());
    }
    return pBuffer;
}
cs

그래서 CCFileUtils 클래스를 상속받은 FileUtils라는 클래스를 만들어 사용

wchar_t로 파일 이름을 변경하고 그 다음에 _wfopen을 통해서 파일을 읽음 

이렇게 하면 한글이 써있는 UTF-8 파일도 제대로 읽을 수 있음 


1
2
nLen = MultiByteToWideChar(CP_UTF8, 0, pszText, nLen, pwszBuffer, nBufLen); 
nLen = MultiByteToWideChar(CP_ACP, 0, pszText, nLen, pwszBuffer, nBufLen);
cs

멀티랭귀지를 사용해야하는데 이런식으로 억지로 바꾸는건 도움이 안됨

File을 제대로 입력을 받아야 근본적인 문제가 해결된다 

생성자에서 CCNotificationCenter를 통해 옵저버를 엄청 많이 등록해 놓고 

사용을 한 뒤 코드에서 그 씬을 재시작 하니깐 신나게 에러가 남 

1
2
3
4
5
6
7
8
9
CCNotificationCenter::sharedNotifCenter()->addObserver(this, callfuncO_selector(MainScene::changeSelectTurnCard), EVENT_BOARD_POP_DICISION_PLAY_TURN, NULL);
CCNotificationCenter::sharedNotifCenter()->addObserver(this, callfuncO_selector(MainScene::turnTheCardOver), EVENT_BOARD_DECIDED_PLAY_TURN, NULL);
CCNotificationCenter::sharedNotifCenter()->addObserver(this, callfuncO_selector(MainScene::turnCardExitCommand), EVENT_BOARD_DECIDED_PLAY_TURN_ANI_END, NULL);
CCNotificationCenter::sharedNotifCenter()->addObserver(this, callfuncO_selector(MainScene::setDiceBTNPosition), EVENT_BOARD_CHANGE_POSITION_DICE, NULL);
CCNotificationCenter::sharedNotifCenter()->addObserver(this, callfuncO_selector(MainScene::viewDiceNumSprite), EVENT_BOARD_DICE_THROW, NULL);
CCNotificationCenter::sharedNotifCenter()->addObserver(this, callfuncO_selector(MainScene::changeTurn), EVENT_BOARD_CHANGE_TURN, NULL);
CCNotificationCenter::sharedNotifCenter()->addObserver(this, callfuncO_selector(MainScene::fadeOutCardPopUpUI), EVENT_BOARD_DECIDED_PLAY_TURN_ANI, NULL);
CCNotificationCenter::sharedNotifCenter()->addObserver(this, callfuncO_selector(MainScene::gameOver), EVENT_INGAME_GAMEOVER, NULL);
CCNotificationCenter::sharedNotifCenter()->addObserver(this, callfuncO_selector(MainScene::checkMovePosition), EVENT_DONE_MOVE_CHARACTER, NULL);
cs
1
CCDirector::sharedDirector()->replaceScene(mainScene);    
cs

원인은 this로 등록했는데 Scene을 재시작 할때 this를 삭제해버리기 때문

두번째 만들어진 Scene에서 등록하려고 해도 이미 있는 옵저버 이름이기 때문에 등록하지 않음

그래서 소멸자에서 현재 Scene에서 등록했던 옵저버들을 제거해 줘야 함

1
2
3
4
5
6
7
8
9
CCNotificationCenter::sharedNotifCenter()->removeObserver(this, EVENT_BOARD_POP_DICISION_PLAY_TURN);
CCNotificationCenter::sharedNotifCenter()->removeObserver(this, EVENT_BOARD_DECIDED_PLAY_TURN);
CCNotificationCenter::sharedNotifCenter()->removeObserver(this, EVENT_BOARD_DECIDED_PLAY_TURN_ANI_END);
CCNotificationCenter::sharedNotifCenter()->removeObserver(this, EVENT_BOARD_CHANGE_POSITION_DICE);
CCNotificationCenter::sharedNotifCenter()->removeObserver(this, EVENT_BOARD_DICE_THROW);
CCNotificationCenter::sharedNotifCenter()->removeObserver(this, EVENT_BOARD_CHANGE_TURN);
CCNotificationCenter::sharedNotifCenter()->removeObserver(this, EVENT_BOARD_DECIDED_PLAY_TURN_ANI);
CCNotificationCenter::sharedNotifCenter()->removeObserver(this, EVENT_INGAME_GAMEOVER);
CCNotificationCenter::sharedNotifCenter()->removeObserver(this, EVENT_DONE_MOVE_CHARACTER);
cs

또 객체에서 등록해 준 옵저버들도 마찬가지로 소멸자에서 제거해준다.





1
2
3
4
5
// 복사 금지 매크로
#define MAKE_NO_COPY(CLASSNAME)                                             \
        private:                                                            \
               CLASSNAME(const CLASSNAME&);                                 \
               CLASSNAME& operator=(const CLASSNAME&);
cs

1
2
3
4
5
6
7
8
9
// 싱클톤 패턴 생성 매크로
#define DECLARE_SINGLETONE(CLASSNAME)                                       \
        MAKE_NO_COPY(CLASSNAME)                                             \
        private:                                                            \
               CLASSNAME() {}                                               \
               static CLASSNAME* sInstance;                                 \
        public:                                                             \
               static CLASSNAME& sharedClass();                                \
               static void purgeSharedClass();
cs

1
2
3
4
5
6
7
8
9
10
11
12
13
 // 싱글톤 패턴 구현 매크로
#define IMPLEMENT_SINGLETON(CLASSNAME)                              \
               CLASSNAME* CLASSNAME::sInstance= NULL;               \
                                                                    \
               CLASSNAME& CLASSNAME::getInstance() {                \
                       if(sInstance == NULL)                        \
                              sInstance=new CLASSNAME;              \
                              return *sInstance;                    \
               }                                                    \
               void CLASSNAME::purgeSharedClass()                    \
               {                                                    \
                    CC_SAFE_RELEASE(CLASSNAME);                        \
               }



cs

자주 사용하는 패턴이기 때문에 매크로로 만들어서 사용하면 편리하고 조으다

싱글톤 패턴 생성 매크로는 헤더에 사용하고

구현 매크로는 cpp파일에서 사용하면 됨


일단 제일 대표적인 싱글톤 클래스는 CCDirector이다.

이건 뭐 어디가든 불변이겠지.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
static CCDisplayLinkDirector s_sharedDirector;
static bool s_bFirstRun = true;
 
CCDirector* CCDirector::sharedDirector(void)
{
    if (s_bFirstRun)
    {
        s_sharedDirector.init();
        s_bFirstRun = false;
    }
 
    return &s_sharedDirector;
}
 
void CCDirector::purgeDirector()
{
    // don't release the event handlers
    // They are needed in case the director is run again
    CCTouchDispatcher::sharedDispatcher()->removeAllDelegates();
 
    if (m_pRunningScene)
    {
        m_pRunningScene->onExit();
        m_pRunningScene->cleanup();
        m_pRunningScene->release();
    }
    
    m_pRunningScene = NULL;
    m_pNextScene = NULL;
 
    // remove all objects, but don't release it.
    // runWithScene might be executed after 'end'.
    m_pobScenesStack->removeAllObjects();
 
    stopAnimation();
 
#if CC_DIRECTOR_FAST_FPS
    CC_SAFE_RELEASE_NULL(m_pFPSLabel);
#endif
 
    CCObject* pProjectionDelegate = (CCObject*)m_pProjectionDelegate;
    CC_SAFE_RELEASE_NULL(pProjectionDelegate);
 
    // purge bitmap cache
    CCLabelBMFont::purgeCachedData();
 
    // purge all managers
    CCAnimationCache::purgeSharedAnimationCache();
     CCSpriteFrameCache::purgeSharedSpriteFrameCache();
    CCActionManager::sharedManager()->purgeSharedManager();
    CCScheduler::purgeSharedScheduler();
    CCTextureCache::purgeSharedTextureCache();
    CCUserDefault::purgeSharedUserDefault();
    CCNotificationCenter::purgeNotifCenter();
    // OpenGL view
    m_pobOpenGLView->release();
    m_pobOpenGLView = NULL;
}
cs

싱글톤을 사용하기 위한 최소한의 조건이다.

shardDirector을 구현해 그 안에서 객체가 생성되어 있는지 확인하고 생성이 되어있지 않다면 생성해 주고 return해주면 된다 

한번 생성한 다음부터는 계속 shardDirector를 호출해서 싱글톤 클래스에 접근한다 

purgeSharedClass를 사용해서 싱글톤으로 설정한 객체에서 사용한 메모리를 해제해 주고 종료하면 된다.


나중에 스케일이 커지면 싱글톤을 만드는 매크로를 만들어도 괜찮지 않을까...





'Cocos2D' 카테고리의 다른 글

CCNotificationCenter을 사용한 Scene 재시작  (0) 2016.03.29
cocos2d-x Singleton(싱글톤) macro  (0) 2016.03.17
Cocos2d-x에서 사용한 Observer Pattern  (0) 2016.03.15
CC_SYNTHESIZE 외 기타 등등등  (0) 2016.03.11
CCAssert  (0) 2016.03.07

CCNotificationCenter는 Objective-C에 "NSNotificationCenter"와 비슷한 기능이다.

근데 난 둘다 모르겠고


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class CC_DLL CCNotificationCenter : public CCObject
{
public:
    CCNotificationCenter();
    ~CCNotificationCenter();
    
    static CCNotificationCenter *sharedNotifCenter(void);
    static void purgeNotifCenter(void);
 
    void addObserver(CCObject *target, 
                     SEL_CallFuncO selector,
                     const char *name,
                     CCObject *obj);
    
    void removeObserver(CCObject *target,const char *name);
    
    void postNotification(const char *name);
    void postNotification(const char *name, CCObject *object);
    
private:
    //
    // internal functions
    //
    bool observerExisted(CCObject *target,const char *name);
    
    //
    // variables
    //
    CCArray *m_observers;
};
cs

로 되어있는 클래스가 있는데

사용할때는 

1
2
3
CCNotificationCenter::sharedNotificationCenter()->addObserver(this, callfuncO_selector(BombView::onCreateBomb), ADD_BOMB, NULL);    
CCNotificationCenter::sharedNotificationCenter()->addObserver(this, callfuncO_selector(BombView::onMoveStep), MOVE_BOMB, NULL);    
CCNotificationCenter::sharedNotificationCenter()->addObserver(this, callfuncO_selector(BombView::onRemoveBomb), REMOVE_BOMB, NULL);
cs

이렇게 등록해놓고 

1
2
3
CCNotificationCenter::sharedNotificationCenter()->postNotification(ADD_BOMB, (CCObject*)&point);
CCNotificationCenter::sharedNotificationCenter()->postNotification(MOVE_BOMB);
CCNotificationCenter::sharedNotificationCenter()->postNotification(REMOVE_BOMB);
cs

이런식으로 사용하면 된다. 



이렇게 우회해서 함수에 접근해야 하는 이유는 

img_01

레이어가 직접적으로 MapScene을 접근하는건 잘못된 일이기 때문이다.

img_02

따라서 이런식으로 접근해야 좀 더 안전하게 접근 할 수 있다. 


NotificationObserver Class는 왜 있는지 모르겠네...



http://wonderpla.net/blog/engineer/CCNotificationCenter

https://github.com/zhouyizirui/Fighter/search?utf8=%E2%9C%93&q=ADD_BOMB


'Cocos2D' 카테고리의 다른 글

cocos2d-x Singleton(싱글톤) macro  (0) 2016.03.17
Cocos2d-x 에서 사용하는 Singleton  (2) 2016.03.15
CC_SYNTHESIZE 외 기타 등등등  (0) 2016.03.11
CCAssert  (0) 2016.03.07
getPositionInPixel 과 getPosition의 차이  (0) 2016.03.04
CC_SYNTHESIZE
1
2
3
4
#define CC_SYNTHESIZE(varType, varName, funName)\
protected: varType varName;\
publicvirtual varType get##funName(voidconst { return varName; }\
publicvirtual void set##funName(varType var){ varName = var; 
cs

CC_SYNTHESIZE_PASS_BY_REF

1
2
3
4
#define CC_SYNTHESIZE_PASS_BY_REF(varType, varName, funName)\
protected: varType varName;\
publicvirtual const varType& get##funName(voidconst { return varName; }\
publicvirtual void set##funName(const varType& var){ varName = var; }
cs

CC_SYNTHESIZE_READ_ONLY

1
2
3
#define CC_SYNTHESIZE_READONLY(varType, varName, funName)\
protected: varType varName;\
publicvirtual varType get##funName(voidconst { return varName; }
cs
CC_SYNTHESIZE_READ_ONLY_PASS_BY_REF
1
2
3
#define CC_SYNTHESIZE_READONLY_PASS_BY_REF(varType, varName, funName)\
protected: varType varName;\
publicvirtual const varType& get##funName(voidconst { return varName; }
cs

CC_SYNTHESIZE_RETAIN
1
2
3
4
5
6
7
8
9
#define CC_SYNTHESIZE_RETAIN(varType, varName, funName)    \
protected: varType varName; \
publicvirtual varType get##funName(voidconst { return varName; } \
publicvirtual void set##funName(varType var)   \
{ \
    CC_SAFE_RETAIN(var); \
    CC_SAFE_RELEASE(varName); \
    varName = var; \
cs


CC_SYNTHESIZE와 CC_SYNTHESIZE_PASS_BY_REF의 차이는

get 함수에 있는데 PASS_BY_REF는 return값이 const verType&이다.

즉 반환해야 하는 값이 클래스라면 PASS_BY_REF을 권장 

받는 쪽에서 복사 생성이 일어나지 않기 때문이다.

int나 double 같은 작은 메모리를 차지하는 것은 그냥 CC_SYNTHESIZE를 통해 get을 해도 되지만

클래스 처럼 메모리를 많이 차지 하는 애들은 복사 생성하는데 시간이 오래걸리기 때문.


CC_SYNTHESIZE_RETAIN은 set을 주의해서 봐야하는데

매번 함수를 호출할때마다 매번 새 포인터를 생성하고 기존에 있던 포인터는 해제한다.







<중국어 주의>

원형은 이거

cond가 true이면 

Assert 에러를 토해냄

1
2
int i = 10;  
CCAssert( i < 9,"i should small than 10");
cs

프로그램이 조건에서 벗어난 경우 프로그램을 중단할수 있기 때문에 사용





getPositionInPixel 은 2.0넘어가면서 없어짐

getPosition을 사용해야함



왠지 검색해도 안나오더라... 쮸글

+ Recent posts