I found good deep copy for cocos2d-x and copyWithZone method(Korean)

sorry, it is so hard to explain in English so I write to Korean
but code is so good, please read it for deep copy in Cocos2d-x
I hope so make CCSprite method and CCNode method for deep copy so I make a clue and write.
now start

(Korean) copyWithZone을 아무도 안 쓰는 사실에 저는 매우 궁금했습니다.
(English) Why CCNode or CCSprite is not used copyWithZone method? so it makes hard to deep copy in game.

(Korean) C의 deep copy는 생성자와 같이 쓰이지만, objective-c를 계승한 cocos2d-x의 deep copy는 약간 다릅니다.
C
is not support automatic deep copy, and objective-c is also not support.

(Korean) copyWithZone이라는 method를 쓰는데, 이것은 생성자가 아니라, 원본이 복사본을 return하는 구조로 되어 있습니다.
(English) objective-c is copyWithZone method. it works by origin class method and it returns copy.

(Korean) 즉 C은 이런 구조이지만
복사 대상 = 복사 대상:생성자;
so C
Style is
Copy* copy = Copy(Origin)

(Korean) Objective-C는 다음과 같은 구조입니다
복사 객체 = 원본 class::copyWithZone(NULL);
(English) but Objective-C Style is
Copy* copy = Origin~~>copyWithZone
복사라는 건 원래 원본이 존재하는 것이 먼저잖아요? 그래서 copyWithZone이 제 철학에 맞아서 더 좋아합니다.
실제로 2 방법 모두 실험 코딩을 해봤는데, copyWithZone이 깔끔하기도 했고요.
I likes to use copyWithZone, because it is more simple and usefully when ever.
그래서 저는 copyWithZone method를 구현하기로 마음 먹었습니다. 하지만 쉽지 않습니다. 일단 4가지 이슈가 있습니다.
so I make a mind for coding copyWithZone method for everyone, but it is hard because 4 issue.

issue 1. C에서 deep copy는 직접 코딩해야 한다.
issue 1. C
doesn’t give a automatic deep copy method, so we must make a method for deep copy by hand!
즉 언어에서 class = class 이런 식으로 deep copy를 지원하지 않는 겁니다. class를 통째로 복사해주면 얼마나 편할까요? 하지만 그게 안됩니다.
class = C*+ is not support operator for deep copy like “class = class” , I think want deep copy method “=”, but It is just dream in C*+
Hero* Hero::copyWithZone
{
pRet~~>life = life;
pRet~~>m_season = m_season;
pRet~~>m_level = m_level;
}
(Korean) 이런 식으로 일일이 복사해야 한다는 것이죠.
(English) so We must make a method “copyWithZone” and using this. so very very inconvenience way! but we must do it.

(Korean) issue 2. 더 힘든 것은 class안에 다른 class가 멤버 변수로 있으면 정말 골치가 아픕니다.
그러면 그것도 copyWithZone을 만들어야 하고, 만약에 그것이 외부 라이브러리라서 건들지도 못하면 천재 프로그래머라고 해도 죽어납니다. 아니 불가능할 겁니다.
(혹시 방법이 있으면 저에게 방법을 알려주세요. ㅠ_ㅠ)
(English) issue 2. If class have a member class, It will be big problem. maybe it must be static library or something that you never handle. hm…. project will be must stop…
(please help me about this issue 2)

이 문제는 좀 복잡한데, reference counter 문제가 생기는 경우에는 class의 멤버 클레스의 copyWithZone에서 문제가 발생할 확률이 높습니다. 예를 들어 class 내부에 timer class가 있고 이것이 copy본의 reference counter나 origin의 reference counter을 잘못 더하는 설계상 오류가 원인입니다.

따라서 멤버 클래스의 오류로 인하여 발생하는 문제이므로 m_uReference와 m_uAutoReleaseCount를 복사할 필요는 없습니다. 복사하면 오히려 위험하니 절대 건들지 마세요.

  1. 상속에서 retain, release 문제
    copyWithZone은 retain과 release를 잘 못 쓰면, 치명적인 메모리 문제를 발생할 수 있습니다.
    CCNode:;addChild나 arrayList에서 data없다고 뻑나는 거 일도 아닙니다.
    하지만 잘 맞춰주면 최고의 안정성을 보여줄 수 있을 겁니다.

  2. 상속 받았을 경우, 부모 class에도 copyWithZone을 해줘야 한다.

상속 관계
CCSprite > MasterTarget> MasterCharacter ~~> Hero
상속이 다음과 같다면, 당연히 Hero class만 아니라 나머지 2개의 부모 class에도 copyWithZone을 만들어 줘야 합니다.
CCSprite는 copyWithZone이 없기 때문에 CCSprite 안에 있는 변수는 복사할 수 없습니다.
따라서 MasterTarget에서 CCSprite의 내용을 모두 복사해줘야 합니다. 그래서 모두 cocos2d-x에 건의를 합시다.
cocos2d-x의 모든 class에 deep copy method를 만들어 달라!

그러면 이제부터 시작하겠습니다.

Hero* Hero::copyWithZone
{
CCZone pNewZone = NULL;
Hero
pRet = NULL;
if //in case of being called at sub class
{
pRet = ;
}
else
{
pRet = new Hero;
pZone = pNewZone = new CCZone;
// auto release
pRet~~>autorelease();
}

// call parent method
MasterCharacter::copyWithZone(pZone);

// copy member data
if ( pRet && pRet~~>init )
{
for
{
pRet~~>isSkillsList[i] = isSkillsList[i];
}

pRet~~>m_imagePath = m_imagePath;
pRet~~>life = life;
pRet~~>m_season = m_season;
pRet~~>m_level = m_level;
pRet~~>m_bulletLevel = m_bulletLevel;
pRet~~>isHeroHot = isHeroHot;
pRet->isHeroHoly = isHeroHoly;

for(int i = 0; i < m_state.size(); i**)
{
master::State* state = master::State::create, m_state[i]>getLifeTime);
pRet
>m_state.push_back;
pRet~~>addChild;
}
for; i**)
{
Timer* timer = m_timer[i]>copyWithZone;
pRet
>m_timer.push_back;
pRet->addChild;
}
for; i**)
{
int id = m_equipItem[i]>getType.id;
pRet
>setEquipItem;
}
return pRet;
}
else
{
CC_SAFE_DELETE;
returnNULL;
}
}
코드를 보시면 Hero class는 Timer라는 class를 멤버 변수로 가지고 있습니다. 어라 이거!
이슈 2.번 케이스입니다.
따라서 Timer에서 copyWithZone을 만들어 줍니다.
Timer까지 코드를 쓰지는 않겠습니다. 너무 길거든요. 어차피 같은 방법이라서 넘어갑니다.
Timer* timer = m_timer[i]>copyWithZone;
pRet
>m_timer.push_back;
pRet~~>addChild;
흠 다음 코드는 이슈 3번 케이스입니다.
else
{
pRet = new Hero;
pZone = pNewZone = new CCZone;
// auto release
pRet~~>autorelease;
}
어째서 if에서는 autorelease가 없는데, 어째서 else에서는 autorelease가 있을까요?
if //in case of being called at sub class
{
pRet = ;
}
if의 목적은 자식 class에서 copyWithZone을 호출했으면 실행되는 것이고,
else 문의 목적은 현재 class가 copyWithZone을 처음 호출할 때 실행이 되는 겁니다.
if 문이 실행되는 과정을 좀 살펴보도록 하겠습니다.
CCSprite~~> MasterTarget > MasterCharacter> Hero > Knight 라는 상속 관계가 있다면
Knight
>copyWithZone로 호출하게 되면 Hero~~>copyWithZone을 호출하게 됩니다.
어째서 그렇게 될까요?
처음에 실행하면 pZone은 NULL이 되고, NULL이 되면 if은 false가 되어서 if를 건너뛰고, else문을 실행하게 됩니다.
Knight~~>copyWithZone
{
자세한 코드는 생략한다.
CCZone **pNewZone = NULL;
if //in case of being called at sub class
{
// 당연히 처음 실행하면 pZone == NULL이기 때문에 이 부분은 실행이 안된다.
}
else
{
// 그러면 else로 가게 되고, 그러면 CCZone이 생성되어 Hero~~>copyWithZone에서는 if가 실행되는 것이다.
pRet = new Hero;
pZone = pNewZone = new CCZone;
// auto release
pRet~~>autorelease;
}
Hero~~>copyWithZone
}

else문에서는 pZone을 생성하게 되므로, Hero~~>copyWithZone에서는 if문이 실행하게 됩니다.
우와 그러면 MasterCharacter에서는 어떻게 될까요?
MasterCharacter에서는 if 부분이 실행
MasterTarget에서도 if 부분이 실행
즉 부모 클래스는 if문이고, 처음 호출한 자식 class는 else문이 실행되는 구조입니다.
와 그러면~ 끝났나요? 아니요.
가장 최상의 부모는 코드가 약간 다릅니다. 최종적으로 도착한 부모 클래스는 다음과 같이 없는 것이 있습니다.
다음 상위 부모 클래스의 copyWithZone을 호출하는 것이 없다.
return 부분이 다르다.
else
{
CC_SAFE_DELETE;
returnNULL;
}
MasterTarget** MasterTarget::copyWithZone
{
CCZone pNewZone = NULL;
MasterTarget
pRet = NULL;
if //in case of being called at sub class
{
pRet = ;
}
else
{
pRet = new MasterTarget;
pZone = pNewZone = new CCZone;
pRet~~>autorelease;
}
// call parent method
// copy member data
if )
{
// CCNode
pRet~~>m_nTag = m_nTag;
CC_SAFE_DELETE;
return pRet;
}
}

자 이제 만들 수 있을 힌트는 다 드렸을까요?
아니요
이슈 3번 문제입니다.
상속에서 retain, release 문제

제가 최종적으로 이 부분에서 애를 먹었지만 다음과 같이 수정하였기에 문제가 없었습니다.
if //in case of being called at sub class
{
pRet = ;
}
else
{
pRet = new MasterTarget;
pZone = pNewZone = new CCZone;
pRet~~>autorelease;
}
하지만 만일 이렇게 수정했다면 어떻게 될까요?
if //in case of being called at sub class
{
pRet = ;
}
else
{
pRet = new MasterTarget;
pZone = pNewZone = new CCZone;
} pRet~~>autorelease;
망합니다.

CCSprite > MasterTarget> MasterCharacter > Hero 로 차근 차근 오면서
autorelease로 reference counter를 하나씩 까먹게 됩니다.
따라서 상속이 된 만큼 retain을 해야 하는데, 그게 좋은 방법은 아니죠.
따라서 가장 처음 호출한 시점에서 autorelase를 딱 한번 해주면 됩니다.
즉 else에서 딱 1번만 해주면 된다는 겁니다.
자 이제 사용해봅시다.
CCPoint pos = hero
>getPosition;
Hero* tempHero = hero~~>copyWithZone;
tempHero~~>setPosition;
this->addChild;
자 복사한 내용은 동일하게 되었습니다.
복사 객체 tempHero
원본 객체 Hero

연구 기일: 내 휴일* 내 주말* 내 경력 + 3일 흐어엉 ㅠ_ㅜ
주님께 영광을 돌리며, 오늘 칼퇴할 수 있기를 !


스크린샷 2013-07-17 오전 11.35.51.png (45.7 KB)