cocos2d-x 源码剖析(10)
2014-03-10 14:25
357 查看
上一节讲CCActionInterval,这节讲CCActionInstant。同样为了方便,我们用CCPlace来讲解。作为了解过CCActionInterval的过来人来说,CCActionInstant意外的简单。还记得这两种Aciton之间的区别吗?前者需要一个duration,而后者调用一次就介绍了,自然也不用什么持续时间了。翻看源码,可以发现CCActionInstant和CCActionInterval是一套逻辑,毕竟他们都是CCActionInstant的子类。那么可以说所有的CCActionInstant都是这套逻辑了。下面是CCPlace的全部实现:
而在CCAction之中isDone被默认实现为:
所以它的逻辑就是调用一次就删,就这么简单。这节有点短,我们再抓几个例子充数吧。比如说CCCallFunc。我之所以抓它来讲,是因为这个action很危险。而且他也有特殊的地方,比如说它的init函数:
看吧,他把Target retain了一次。init函数还避免掉了重复调用可能出现的bug。这个小特殊没有什么大不了,其危险之处在于采用函数指针来实现回调。
C++被设计为尽早在编译期发现错误,如果你使用函数指针,编译器不太容易发现你的误用。而且一旦误用,就会发生未定义的行为,运行不一定马上崩溃,这种bug是最要命的。好在最新的代码中已经使用function来实现回调。这种实现一定要引以为戒。
还有一个特殊的CCActionInstant action是CCRemoveSelf。这种Action是很方便的,但是我对它的安全性保持怀疑。它的作用是调用一次target的release,而且在init的时候没有将target retain,这有点小hack了。当然整个action的设计都不咋滴。我在前面忘记讲removeFromParentAndCleanup这个函数了,乘此机会来补一下。
这个函数本身没什么,如果参数为true则调用cleanup函数,而且默认是为true的。
从Parent中移去大体上是这个流程,先调用自己的onExitTransitionDidStart,导致所有的子Node的
onExitTransitionDidStart都调用一遍,调用onExit,导致所有子Node的onExit都调用一遍,调用自己cleanup,导致所有的子Node的cleanup都调用一遍。最后从父容器中移去,也就是引用计数减1.如果引用计数到0,导致自己析构函数调用。这时他会删除自己Children容器,导致所有的子Node的引用计数都减1.
我们知道能对action有影响的是只有cleanup函数,如果在step中调用到了cleanup这个函数,那么就相当于在action中调用了removeAllActionsFromTarget这个函数。前面讲了一个removeActionAtIndex。说这个函数在step中调用是安全的。而且分析了这个函数是最终一个action的step的情况,也是安全的。我们现在分析一下这个函数,算作温习:
之前说过在step中删除其他节点是安全的,那么有问题的只可能是ccArrayRemoveAllObjects(pElement->actions);这里的情况有2个,如果这个action是最后一个action。循环不再,不会有问题。如果这个action不是最后一个action。之前那个函数不会有问题,是因为它无耻的手动回减了一次index。这里调用了一下ccArrayRemoveAllObjects(pElement->actions),将actions的num减为0,那么在下一次循环判断为false,不再循环也就没什么问题了。
CCPlace* CCPlace::create(const CCPoint& pos) { CCPlace *pRet = new CCPlace(); if (pRet && pRet->initWithPosition(pos)) { pRet->autorelease(); return pRet; } CC_SAFE_DELETE(pRet); return NULL; } bool CCPlace::initWithPosition(const CCPoint& pos) { m_tPosition = pos; return true; } CCObject * CCPlace::copyWithZone(CCZone *pZone) { CCZone *pNewZone = NULL; CCPlace *pRet = NULL; if (pZone && pZone->m_pCopyObject) { pRet = (CCPlace*) (pZone->m_pCopyObject); } else { pRet = new CCPlace(); pZone = pNewZone = new CCZone(pRet); } CCActionInstant::copyWithZone(pZone); pRet->initWithPosition(m_tPosition); CC_SAFE_DELETE(pNewZone); return pRet; } void CCPlace::update(float time) { CC_UNUSED_PARAM(time); m_pTarget->setPosition(m_tPosition); }
而在CCAction之中isDone被默认实现为:
bool CCAction::isDone() { return true; }
所以它的逻辑就是调用一次就删,就这么简单。这节有点短,我们再抓几个例子充数吧。比如说CCCallFunc。我之所以抓它来讲,是因为这个action很危险。而且他也有特殊的地方,比如说它的init函数:
bool CCCallFunc::initWithTarget(CCObject* pSelectorTarget) { if (pSelectorTarget) { pSelectorTarget->retain(); } if (m_pSelectorTarget) { m_pSelectorTarget->release(); } m_pSelectorTarget = pSelectorTarget; return true; }
看吧,他把Target retain了一次。init函数还避免掉了重复调用可能出现的bug。这个小特殊没有什么大不了,其危险之处在于采用函数指针来实现回调。
CCObject* m_pSelectorTarget; intm_nScriptHandler; union { SEL_CallFunc m_pCallFunc; SEL_CallFuncN m_pCallFuncN; SEL_CallFuncND m_pCallFuncND; SEL_CallFuncO m_pCallFuncO; };
C++被设计为尽早在编译期发现错误,如果你使用函数指针,编译器不太容易发现你的误用。而且一旦误用,就会发生未定义的行为,运行不一定马上崩溃,这种bug是最要命的。好在最新的代码中已经使用function来实现回调。这种实现一定要引以为戒。
还有一个特殊的CCActionInstant action是CCRemoveSelf。这种Action是很方便的,但是我对它的安全性保持怀疑。它的作用是调用一次target的release,而且在init的时候没有将target retain,这有点小hack了。当然整个action的设计都不咋滴。我在前面忘记讲removeFromParentAndCleanup这个函数了,乘此机会来补一下。
void CCNode::removeFromParentAndCleanup(bool cleanup) { if (m_pParent != NULL) { m_pParent->removeChild(this,cleanup); } }
void CCNode::removeChild(CCNode* child,bool cleanup) { // explicit nil handling if (m_pChildren==NULL) { return; } if(m_pChildren->containsObject(child)) { this->detachChild(child,cleanup); } }
void CCNode::detachChild(CCNode *child, bool doCleanup) { // IMPORTANT: // -1st do onExit // -2nd cleanup if (m_bRunning) { child->onExitTransitionDidStart(); child->onExit(); } // If you don't do cleanup, the child's actions will // not get removed and the // its scheduledSelectors_ dict will not get released! if (doCleanup) { child->cleanup(); } // set parent nil at the end child->setParent(NULL); m_pChildren->removeObject(child); }
这个函数本身没什么,如果参数为true则调用cleanup函数,而且默认是为true的。
void CCNode::cleanup() { // actions this->stopAllActions(); this->unscheduleAllSelectors(); if(m_eScriptType!=kScriptTypeNone) { CCScriptEngineManager::sharedManager()-> getScriptEngine()->executeNodeEvent(this,kCCNodeOnCleanup); } // timers arrayMakeObjectsPerformSelector(m_pChildren,cleanup,CCNode*); }
从Parent中移去大体上是这个流程,先调用自己的onExitTransitionDidStart,导致所有的子Node的
onExitTransitionDidStart都调用一遍,调用onExit,导致所有子Node的onExit都调用一遍,调用自己cleanup,导致所有的子Node的cleanup都调用一遍。最后从父容器中移去,也就是引用计数减1.如果引用计数到0,导致自己析构函数调用。这时他会删除自己Children容器,导致所有的子Node的引用计数都减1.
我们知道能对action有影响的是只有cleanup函数,如果在step中调用到了cleanup这个函数,那么就相当于在action中调用了removeAllActionsFromTarget这个函数。前面讲了一个removeActionAtIndex。说这个函数在step中调用是安全的。而且分析了这个函数是最终一个action的step的情况,也是安全的。我们现在分析一下这个函数,算作温习:
void CCActionManager::removeAllActionsFromTarget(CCObject* pTarget) { // explicit null handling if(pTarget==NULL) { return; } tHashElement* pElement=NULL; HASH_FIND_INT(m_pTargets,&pTarget,pElement); if(pElement) { if(ccArrayContainsObject(pElement->actions, pElement->currentAction)&&(!pElement->currentActionSalvaged)) { pElement->currentAction->retain(); pElement->currentActionSalvaged=true; } ccArrayRemoveAllObjects(pElement->actions); if(m_pCurrentTarget==pElement) { m_bCurrentTargetSalvaged=true; } else { deleteHashElement(pElement); } } else { // CCLOG("cocos2d: removeAllActionsFromTarget: Target not found"); } }
之前说过在step中删除其他节点是安全的,那么有问题的只可能是ccArrayRemoveAllObjects(pElement->actions);这里的情况有2个,如果这个action是最后一个action。循环不再,不会有问题。如果这个action不是最后一个action。之前那个函数不会有问题,是因为它无耻的手动回减了一次index。这里调用了一下ccArrayRemoveAllObjects(pElement->actions),将actions的num减为0,那么在下一次循环判断为false,不再循环也就没什么问题了。
相关文章推荐
- 【cocos2d-x 源码剖析】启动窗口
- cocos2d-x 源码剖析(3)
- cocos2d-x 源码剖析(7)
- cocos2d-x 源码剖析(4)
- cocos2d-x 源码剖析(8)
- cocos2d-x开发日志10 ——贪食蛇源码
- cocos2d-x 源码剖析(5)
- cocos2d-x 源码剖析(9)
- cocos2d-x 源码剖析(11)
- cocos2d-x源码剖析之场景管理
- cocos2d-x 源码剖析(6)
- cocos2d-x 源码剖析(12)
- cocos2d-x 源码剖析(13)
- cocos2d-x源码剖析引子
- cocos2d-x 源码剖析(14)
- cocos2d-x源码剖析之精灵对象
- cocos2d-x应用窗口相关源码剖析1
- cocos2d-x 源码剖析(15)
- cocos2d-x 源码剖析(16)
- cocos2d-x源码剖析之整体框架