源代码版本号 3.1r,转载请注明
我也最终不out了,開始看3.x的源代码了。此时此刻的心情仅仅能是wtf!
!!!!!!!
!。只是也最终告别CC时代了。
cocos2d-x 源代码分析文件夹
http://blog.csdn.net/u011225840/article/details/31743129
1.继承结构
没错。是两张图。(你没有老眼昏花。
。我脑子也没有秀逗。。)Ref就是原来的CCObject。而Timer类是与Scheduler类密切相关的类,所以须要把他们放在一起说。Timer和Scheduler的关系就像Data和DataManager的关系。
2.源代码分析
2.1 Timer
2.1.1 Timer中的数据
//_elapsed 上一次运行后到如今的时间 //timesExecuted 运行的次数 //interval 运行间隔 //useDelay 是否使用延迟运行 float _elapsed; bool _runForever; bool _useDelay; unsigned int _timesExecuted; unsigned int _repeat; //0 = once, 1 is 2 x executed float _delay; float _interval;
2.1.2 Update函数
void Timer::update(float dt) { //update方法使用的是模板设计模式,将trigger与cancel的实现交给子类。 if (_elapsed == -1) { _elapsed = 0; _timesExecuted = 0; } //四种情况 /* 1.永久运行而且不使用延迟:基本使用方法。计算elapsed大于interval后运行一次,永不cancel。 2.永久运行而且使用延迟:当elapsed大于延迟时间后,运行一次后,进入情况1. 3.不永久运行而且不使用延迟:情况1结束后,会推断运行次数是否大于反复次数,大于后则cancel。 4.不永久运行而且使用延迟:情况2结束后,进入情况3. */ else { if (_runForever && !_useDelay) {//standard timer usage _elapsed += dt; if (_elapsed >= _interval) { trigger(); _elapsed = 0; } } else {//advanced usage _elapsed += dt; if (_useDelay) { if( _elapsed >= _delay ) { trigger(); _elapsed = _elapsed - _delay; _timesExecuted += 1; _useDelay = false; } } else { if (_elapsed >= _interval) { trigger(); _elapsed = 0; _timesExecuted += 1; } } if (!_runForever && _timesExecuted > _repeat) { //unschedule timer cancel(); } } } }正如我凝视中所说,update使用了模板方法的设计模式思想。将trigger与cancel调用的过程写死,可是不同的子类实现trigger和cancel的方式不同。
2.2 TimerTargetSelector && TimerTargetCallback
void TimerTargetSelector::trigger() { if (_target && _selector) { (_target->*_selector)(_elapsed); } } void TimerTargetCallback::trigger() { if (_callback) { _callback(_elapsed); } }
Ref* _target; SEL_SCHEDULE _selector; ------ ------------------------ void* _target; ccSchedulerFunc _callback; std::string _key;
2.3 Scheduler
2.3.1 Schedule && UnSchedule
typedef struct _hashSelectorEntry { ccArray *timers; void *target; int timerIndex; Timer *currentTimer; bool currentTimerSalvaged; bool paused; UT_hash_handle hh; } tHashTimerEntry;
在这个结构里,target是key值,其它都是数据(除了hh哦)。
timers存放着该target相关的全部timer。currentTimerSalvaged的作用是假设你想停止unschedule正在运行的timer时。会将其从timers移除。并retain,防止被自己主动回收机制回收。然后将此标识为true。以下来看下第一种TimerCallback的Schedule。
void Scheduler::schedule(const ccSchedulerFunc& callback, void *target, float interval, unsigned int repeat, float delay, bool paused, const std::string& key) { CCASSERT(target, "Argument target must be non-nullptr"); CCASSERT(!key.empty(), "key should not be empty!"); //先在hash中查找该target(key值)是否已经有数据 tHashTimerEntry *element = nullptr; HASH_FIND_PTR(_hashForTimers, &target, element); //没有就创建一个。而且将其加入 if (! element) { element = (tHashTimerEntry *)calloc(sizeof(*element), 1); element->target = target; HASH_ADD_PTR(_hashForTimers, target, element); // Is this the 1st element ? Then set the pause level to all the selectors of this target element->paused = paused; } else { CCASSERT(element->paused == paused, ""); } //第一次创建target的数据。需要将timers初始化 if (element->timers == nullptr) { element->timers = ccArrayNew(10); } else { //在timers中查找timer,看在该target下的全部timer绑定的key值是否存在,假设存在,设置新的interval后返回。 //这里必需要解释下,target是hash表的key值。用来查找timers等数据。 //而TimerCallback类型的timer本身含有一个key值(std::string类型),用来标识该唯一timer for (int i = 0; i < element->timers->num; ++i) { TimerTargetCallback *timer = static_cast<TimerTargetCallback*>(element->timers->arr[i]); if (key == timer->getKey()) { CCLOG("CCScheduler#scheduleSelector. Selector already scheduled. Updating interval from: %.4f to %.4f", timer->getInterval(), interval); timer->setInterval(interval); return; } } ccArrayEnsureExtraCapacity(element->timers, 1); } //假设TimerCallback原本不存在在timers中。就加入新的 TimerTargetCallback *timer = new TimerTargetCallback(); timer->initWithCallback(this, callback, target, key, interval, repeat, delay); ccArrayAppendObject(element->timers, timer); timer->release(); }
if (selector == timer->getSelector()) { CCLOG("CCScheduler#scheduleSelector. Selector already scheduled. Updating interval from: %.4f to %.4f", timer->getInterval(), interval); timer->setInterval(interval); return; }
void Scheduler::unschedule(SEL_SCHEDULE selector, Ref *target) { // explicity handle nil arguments when removing an object if (target == nullptr || selector == nullptr) { return; } //CCASSERT(target); //CCASSERT(selector); tHashTimerEntry *element = nullptr; HASH_FIND_PTR(_hashForTimers, &target, element); //假设该target存在数据,就进行删除操作。if (element) { //遍历寻找 for (int i = 0; i < element->timers->num; ++i) { TimerTargetSelector *timer = static_cast<TimerTargetSelector*>(element->timers->arr[i]); //假设正在运行的Timer是须要被unschedule的timer,将其移除而且标识当前正在运行的Timer须要被移除状态为true。 if (selector == timer->getSelector()) { if (timer == element->currentTimer && (! element->currentTimerSalvaged)) { element->currentTimer->retain(); element->currentTimerSalvaged = true; } ccArrayRemoveObjectAtIndex(element->timers, i, true); // update timerIndex in case we are in tick:, looping over the actions if (element->timerIndex >= i) { element->timerIndex--; } //当前timers中不再含有timer。可是假设正在运行的target是该target,则将正在运行的target将被清除标识为true //否则,能够直接将其从hash中移除 if (element->timers->num == 0) { if (_currentTarget == element) { _currentTargetSalvaged = true; } else { removeHashElement(element); } } return; } } } }
同理反观TimerTargetCallback,查找时须要用到std::string。这里不再赘述。
2.3.2 Scheduler的两种定时模式
(此外。模式2还引入了优先级的概念)
template <class T> void scheduleUpdate(T *target, int priority, bool paused) { this->schedulePerFrame([target](float dt){ target->update(dt); }, target, priority, paused); }
// A list double-linked list used for "updates with priority" typedef struct _listEntry { struct _listEntry *prev, *next; ccSchedulerFunc callback; void *target; int priority; bool paused; bool markedForDeletion; // selector will no longer be called and entry will be removed at end of the next tick } tListEntry; typedef struct _hashUpdateEntry { tListEntry **list; // Which list does it belong to ?tListEntry *entry; // entry in the list void *target; ccSchedulerFunc callback; UT_hash_handle hh; } tHashUpdateEntry;
tListEntry,是一个双向链表,target是key。markedForDeletion来告诉scheduler是否须要删除他。tHashUpdateEntry是一个哈希表。通过target能够高速查找到对应的tListEntry。
能够注意到。HashEntry中有个List,来表示该entry属于哪个list。在scheduler中,一共同拥有三个updateList,依据优先级分为negativeList,0List,positiveList,值越小越先运行。
void Scheduler::schedulePerFrame(const ccSchedulerFunc& callback, void *target, int priority, bool paused) { //先检查hash中是否存在该target,假设存在,则将其deleteion的标识 置为false后返回。(可能某个操作将其置为true。而且 //scheduler还没来得及删除,所以这里仅仅须要再改为false就可以) tHashUpdateEntry *hashElement = nullptr; HASH_FIND_PTR(_hashForUpdates, &target, hashElement); if (hashElement) { #if COCOS2D_DEBUG >= 1 CCASSERT(hashElement->entry->markedForDeletion,""); #endif // TODO: check if priority has changed! hashElement->entry->markedForDeletion = false; return; } // most of the updates are going to be 0, that's way there // is an special list for updates with priority 0 //英文凝视解释了为啥有一个0List。if (priority == 0) { appendIn(&_updates0List, callback, target, paused); } else if (priority < 0) { priorityIn(&_updatesNegList, callback, target, priority, paused); } else { // priority > 0 priorityIn(&_updatesPosList, callback, target, priority, paused); } }
void Scheduler::appendIn(_listEntry **list, const ccSchedulerFunc& callback, void *target, bool paused) { //为该target新建一个listEntry tListEntry *listElement = new tListEntry(); listElement->callback = callback; listElement->target = target; listElement->paused = paused; listElement->markedForDeletion = false; DL_APPEND(*list, listElement); // update hash entry for quicker access //而且为该target建立一个高速存取的target tHashUpdateEntry *hashElement = (tHashUpdateEntry *)calloc(sizeof(*hashElement), 1); hashElement->target = target; hashElement->list = list; hashElement->entry = listElement; HASH_ADD_PTR(_hashForUpdates, target, hashElement); }
void Scheduler::priorityIn(tListEntry **list, const ccSchedulerFunc& callback, void *target, int priority, bool paused) { //同理,为target建立一个entry tListEntry *listElement = new tListEntry(); listElement->callback = callback; listElement->target = target; listElement->priority = priority; listElement->paused = paused; listElement->next = listElement->prev = nullptr; listElement->markedForDeletion = false; // empty list ?if (! *list) { DL_APPEND(*list, listElement); } else { bool added = false; //依据优先级。将element放在一个合适的位置,标准的有序链表插入操作,不多解释。
for (tListEntry *element = *list; element; element = element->next) { if (priority < element->priority) { if (element == *list) { DL_PREPEND(*list, listElement); } else { listElement->next = element; listElement->prev = element->prev; element->prev->next = listElement; element->prev = listElement; } added = true; break; } } // Not added? priority has the higher value. Append it. if (! added) { DL_APPEND(*list, listElement); } } // update hash entry for quick access tHashUpdateEntry *hashElement = (tHashUpdateEntry *)calloc(sizeof(*hashElement), 1); hashElement->target = target; hashElement->list = list; hashElement->entry = listElement; HASH_ADD_PTR(_hashForUpdates, target, hashElement); }
// // "updates with priority" stuff // struct _listEntry *_updatesNegList; // list of priority < 0 struct _listEntry *_updates0List; // list priority == 0 struct _listEntry *_updatesPosList; // list priority > 0 struct _hashUpdateEntry *_hashForUpdates; // hash used to fetch quickly the list entries for pause,delete,etc
void Scheduler::unscheduleUpdate(void *target) { if (target == nullptr) { return; } tHashUpdateEntry *element = nullptr; HASH_FIND_PTR(_hashForUpdates, &target, element); if (element) { if (_updateHashLocked) { element->entry->markedForDeletion = true; } else { this->removeUpdateFromHash(element->entry); } } }
update函数会在后面介绍,以下,继续看unschedule的其它方法。
void Scheduler::unscheduleAllForTarget(void *target) { // explicit nullptr handling if (target == nullptr) { return; } // Custom Selectors tHashTimerEntry *element = nullptr; HASH_FIND_PTR(_hashForTimers, &target, element); if (element) { if (ccArrayContainsObject(element->timers, element->currentTimer) && (! element->currentTimerSalvaged)) { element->currentTimer->retain(); element->currentTimerSalvaged = true; } ccArrayRemoveAllObjects(element->timers); if (_currentTarget == element) { _currentTargetSalvaged = true; } else { removeHashElement(element); } } // update selector unscheduleUpdate(target); }该方法会移除target相关的全部定时。包含update类型的,包含Custom Selector类型的,和其它的一样,须要注意该标志位。
2.3.3 定时器的更新update
void Scheduler::update(float dt) { _updateHashLocked = true; //timeScale是什么意思呢,正常的速度是1.0,假设你想二倍速放就设置成2.0,假设你想慢慢放。就设置成0.5. if (_timeScale != 1.0f) { dt *= _timeScale; } // // Selector callbacks // // Iterate over all the Updates' selectors tListEntry *entry, *tmp; //首先处理update类型的定时,你能够发现想调用它的callback,必须满足markedForDeletion为false,从而证明我上面的说法。 // updates with priority < 0 DL_FOREACH_SAFE(_updatesNegList, entry, tmp) { if ((! entry->paused) && (! entry->markedForDeletion)) { entry->callback(dt); } } // updates with priority == 0 DL_FOREACH_SAFE(_updates0List, entry, tmp) { if ((! entry->paused) && (! entry->markedForDeletion)) { entry->callback(dt); } } // updates with priority > 0 DL_FOREACH_SAFE(_updatesPosList, entry, tmp) { if ((! entry->paused) && (! entry->markedForDeletion)) { entry->callback(dt); } } //处理custom类型的定时 // Iterate over all the custom selectors for (tHashTimerEntry *elt = _hashForTimers; elt != nullptr; ) { _currentTarget = elt; _currentTargetSalvaged = false; //没有被暂停。则能够处理 if (! _currentTarget->paused) { // The 'timers' array may change while inside this loop //循环内是当前target下的全部Timer for (elt->timerIndex = 0; elt->timerIndex < elt->timers->num; ++(elt->timerIndex)) { elt->currentTimer = (Timer*)(elt->timers->arr[elt->timerIndex]); elt->currentTimerSalvaged = false; elt->currentTimer->update(dt); //假设currentTimer的update本身内部。在一定条件下unSchedule了本身,则会改变currentTimerSalvaged的标识信息。 //所以要再次进行推断,这就是循环上面英文凝视所述之意 if (elt->currentTimerSalvaged) { // The currentTimer told the remove itself. To prevent the timer from // accidentally deallocating itself before finishing its step, we retained // it. Now that step is done, it's safe to release it. elt->currentTimer->release(); } elt->currentTimer = nullptr; } } // elt, at this moment, is still valid // so it is safe to ask this here (issue #490) elt = (tHashTimerEntry *)elt->hh.next; // only delete currentTarget if no actions were scheduled during the cycle (issue #481) //即使在大循环開始时_currentTargetSalvaged被设置为false。如今的值也可能由于上面该target的各种定时函数调用导致其为true if (_currentTargetSalvaged && _currentTarget->timers->num == 0) { removeHashElement(_currentTarget); } } //这些update类型的定时要被删除咯~~ // delete all updates that are marked for deletion // updates with priority < 0 DL_FOREACH_SAFE(_updatesNegList, entry, tmp) { if (entry->markedForDeletion) { this->removeUpdateFromHash(entry); } } // updates with priority == 0 DL_FOREACH_SAFE(_updates0List, entry, tmp) { if (entry->markedForDeletion) { this->removeUpdateFromHash(entry); } } // updates with priority > 0 DL_FOREACH_SAFE(_updatesPosList, entry, tmp) { if (entry->markedForDeletion) { this->removeUpdateFromHash(entry); } } _updateHashLocked = false; _currentTarget = nullptr; }
所以在每次循环之后,都会推断这些状态位,假设被改变,须要做什么操作。
在代码凝视中,我已经说明。
2.3.4 状态查询与暂停恢复
就是更改状态而已。。
2.3.5 3.x的新特性
This function is thread safe.
@since v3.0
*/
void performFunctionInCocosThread( const std::function<void()> &function);