cocos定时器分析

注:COCOS分析的版本为3.4

COCOS的定时器是通过一个哈希表进行保存的,每一帧循环的时候都会调用定时器的update方法,并传入两帧之间的时间间隔 /*void Scheduler::update(float dt)*/,在update方法中对哈希表进行轮询调用回调函数。

在轮询调用中将定时器分为四种,其中三种是每一帧必定循环调用的方法,struct _listEntry *_updatesNegList;        // list of priority < 0
    struct _listEntry *_updates0List;            // list priority == 0    struct _listEntry *_updatesPosList;        // list priority > 0,简称为系统调用的定时器,例外一种也叫做客户模式,是通过调用管理器的update方法。

以下是对定时器源码进行的详细分析(摘自:http://blog.csdn.net/u011225840/article/details/32141349,这个分析的挺不错的)

 

1.继承结构

 

 

           没错,是两张图。(你没有老眼昏花。。我脑子也没有秀逗。。)Ref就是原来的CCObject,而Timer类是与Scheduler类密切相关的类,所以需要把他们放在一起说。Timer和Scheduler的关系就像Data和DataManager的关系。

2.源码分析

2.1 Timer

2.1.1 Timer中的数据

Timer类定义了一个行为执行的间隔,执行的次数等,可以理解为定时器的数据类,而具体的定时器的行为,定义在子类中。Timer中的数据如下:
                
[cpp]  view plain copy
 
  1. //_elapsed 上一次执行后到现在的时间  
  2.     //timesExecuted 执行的次数  
  3.     //interval 执行间隔  
  4.     //useDelay 是否使用延迟执行  
  5.     float _elapsed;  
  6.     bool _runForever;  
  7.     bool _useDelay;  
  8.     unsigned int _timesExecuted;  
  9.     unsigned int _repeat; //0 = once, 1 is 2 x executed  
  10.     float _delay;  
  11.     float _interval;  

2.1.2 Update函数

[cpp]  view plain copy
 
  1. void Timer::update(float dt)  
  2. {  
  3.   
  4.     //update方法使用的是模板设计模式,将trigger与cancel的实现交给子类。  
  5.   
  6.   
  7.   
  8.       
  9.     if (_elapsed == -1)  
  10.     {  
  11.         _elapsed = 0;  
  12.         _timesExecuted = 0;  
  13.     }  
  14.     //四种情况  
  15.     /* 
  16.         1.永久执行并且不使用延迟:基本用法,计算elapsed大于interval后执行一次,永不cancel。 
  17.         2.永久执行并且使用延迟:当elapsed大于延迟时间后,执行一次后,进入情况1. 
  18.         3.不永久执行并且不使用延迟:情况1结束后,会判断执行次数是否大于重复次数,大于后则cancel。 
  19.         4.不永久执行并且使用延迟:情况2结束后,进入情况3. 
  20.     */  
  21.     else  
  22.     {  
  23.         if (_runForever && !_useDelay)  
  24.         {//standard timer usage  
  25.             _elapsed += dt;  
  26.             if (_elapsed >= _interval)  
  27.             {  
  28.                 trigger();  
  29.   
  30.                 _elapsed = 0;  
  31.             }  
  32.         }      
  33.         else  
  34.         {//advanced usage  
  35.             _elapsed += dt;  
  36.             if (_useDelay)  
  37.             {  
  38.                 if( _elapsed >= _delay )  
  39.                 {  
  40.                     trigger();  
  41.                       
  42.                     _elapsed = _elapsed - _delay;  
  43.                     _timesExecuted += 1;  
  44.                     _useDelay = false;  
  45.                 }  
  46.             }  
  47.             else  
  48.             {  
  49.                 if (_elapsed >= _interval)  
  50.                 {  
  51.                     trigger();  
  52.                       
  53.                     _elapsed = 0;  
  54.                     _timesExecuted += 1;  
  55.   
  56.                 }  
  57.             }  
  58.   
  59.             if (!_runForever && _timesExecuted > _repeat)  
  60.             {    //unschedule timer  
  61.                 cancel();  
  62.             }  
  63.         }  
  64.     }  
  65. }  
          正如我注释中所说,update使用了模板方法的设计模式思想,将trigger与cancel调用的过程写死,但是不同的子类实现trigger和cancel的方式不同。
           另外需要注意的是,Schedule使用时delay的需求,当有delay与没有delay我在源码中已经分析的很清楚了。
 

2.2 TimerTargetSelector   && TimerTargetCallback

           前者是针对类(继承自Ref)中的method进行定时,而后者是针对function(普通函数)。
           前者绑定的类型是SEL_SCHEDULE(你问我这是什么?)typedef void (Ref::*SEL_SCHEDULE)(float);一个指向Ref类型的method指针,并且该method必须满足参数是float,返回值是void。后者绑定的类型是ccSchedulerFunc---------typedef std::function<void(float)> ccSchedulerFunc;这是虾米?这是c++11的新特性,其实就是一个函数指针。
           从他们实现的trigger方法中可以更好的看清这一切。
[cpp]  view plain copy
 
  1. void TimerTargetSelector::trigger()  
  2. {  
  3.     if (_target && _selector)  
  4.     {  
  5.         (_target->*_selector)(_elapsed);  
  6.     }  
  7. }  
  8.   
  9. void TimerTargetCallback::trigger()  
  10. {  
  11.     if (_callback)  
  12.     {  
  13.         _callback(_elapsed);  
  14.     }  
  15. }  
 
最后说一下,TargetCallback中含有一个key,而前者没有。这在下面的源码分析中会看到。(其实原理很简单,SEL_SCHEDULE可以当成key,ccSchedulerFunc不能,因为前者有唯一的标识,如果你不懂这点,欢迎去复习下c++的指向类中方法的函数指针)
[cpp]  view plain copy
 
  1.    Ref* _target;  
  2.    SEL_SCHEDULE _selector;  
  3. ------  ------------------------  
  4.    void* _target;  
  5.    ccSchedulerFunc _callback;  
  6.    std::string _key;  

 
 

2.3 Scheduler

2.3.1 Schedule && UnSchedule

Schedule有四种重载方法。其中各有两种针对不同的Timer子类,但是都大同小异,在此之前,不得不说一个用的非常多的数据结构 tHashTimerEntry
[cpp]  view plain copy
 
  1. typedef struct _hashSelectorEntry  
  2. {  
  3.     ccArray             *timers;  
  4.     void                *target;  
  5.     int                 timerIndex;  
  6.     Timer               *currentTimer;  
  7.     bool                currentTimerSalvaged;  
  8.     bool                paused;  
  9.     UT_hash_handle      hh;  
  10. } tHashTimerEntry;  
     这用到了开源库uthash,关于该hast的具体用法。请自行谷歌。UT_hash_handle能让我们根据key值找到相应的数据。在这个结构里,target是key值,其他都是数据(除了hh哦)。timers存放着该target相关的所有timer。currentTimerSalvaged的作用是如果你想停止unschedule正在执行的timer时,会将其从timers移除,并retain,防止被自动回收机制回收,然后将此标识为true。下面来看下第一种TimerCallback的Schedule。
 
[cpp]  view plain copy
 
  1. void Scheduler::schedule(const ccSchedulerFunc& callback, void *target, float interval, unsigned int repeat, float delay, bool paused, const std::string& key)  
  2. {  
  3.     CCASSERT(target, "Argument target must be non-nullptr");  
  4.     CCASSERT(!key.empty(), "key should not be empty!");  
  5.     //先在hash中查找该target(key值)是否已经有数据  
  6.     tHashTimerEntry *element = nullptr;  
  7.     HASH_FIND_PTR(_hashForTimers, &target, element);  
  8.     //没有就创建一个,并且将其加入  
  9.     if (! element)  
  10.     {  
  11.         element = (tHashTimerEntry *)calloc(sizeof(*element), 1);  
  12.         element->target = target;  
  13.   
  14.         HASH_ADD_PTR(_hashForTimers, target, element);  
  15.   
  16.         // Is this the 1st element ? Then set the pause level to all the selectors of this target  
  17.         element->paused = paused;  
  18.     }  
  19.     else  
  20.     {  
  21.         CCASSERT(element->paused == paused, "");  
  22.     }  
  23.   
  24.     //第一次创建target的数据,需要将timers初始化  
  25.     if (element->timers == nullptr)  
  26.     {  
  27.         element->timers = ccArrayNew(10);  
  28.     }  
  29.     else   
  30.     {  
  31.         //在timers中查找timer,看在该target下的所有timer绑定的key值是否存在,如果存在,设置新的interval后返回。  
  32.         //这里必须要解释下,target是hash表的key值,用来查找timers等数据。  
  33.         //而TimerCallback类型的timer本身含有一个key值(std::string类型),用来标识该唯一timer  
  34.         for (int i = 0; i < element->timers->num; ++i)  
  35.         {  
  36.             TimerTargetCallback *timer = static_cast<TimerTargetCallback*>(element->timers->arr[i]);  
  37.   
  38.             if (key == timer->getKey())  
  39.             {  
  40.                 CCLOG("CCScheduler#scheduleSelector. Selector already scheduled. Updating interval from: %.4f to %.4f", timer->getInterval(), interval);  
  41.                 timer->setInterval(interval);  
  42.                 return;  
  43.             }          
  44.         }  
  45.         ccArrayEnsureExtraCapacity(element->timers, 1);  
  46.     }  
  47.     //如果TimerCallback原本不存在在timers中,就添加新的  
  48.     TimerTargetCallback *timer = new TimerTargetCallback();  
  49.     timer->initWithCallback(this, callback, target, key, interval, repeat, delay);  
  50.     ccArrayAppendObject(element->timers, timer);  
  51.     timer->release();  
  52. }  
       TimerTargetSelector的Schedule不需要本身在通过key值进行存取。其他部分都与上面相同,唯独在查找是否存在Timer时,直接使用了selector。
 
[cpp]  view plain copy
 
  1. if (selector == timer->getSelector())  
  2.            {  
  3.                CCLOG("CCScheduler#scheduleSelector. Selector already scheduled. Updating interval from: %.4f to %.4f", timer->getInterval(), interval);  
  4.                timer->setInterval(interval);  
  5.                return;  
  6.            }  
        继续看下TimerTargetSelector的unschedule。
[cpp]  view plain copy
 
  1. void Scheduler::unschedule(SEL_SCHEDULE selector, Ref *target)  
  2. {  
  3.     // explicity handle nil arguments when removing an object  
  4.     if (target == nullptr || selector == nullptr)  
  5.     {  
  6.         return;  
  7.     }  
  8.       
  9.     //CCASSERT(target);  
  10.     //CCASSERT(selector);  
  11.       
  12.     tHashTimerEntry *element = nullptr;  
  13.     HASH_FIND_PTR(_hashForTimers, &target, element);  
  14.     //如果该target存在数据,就进行删除操作。  
  15.     if (element)  
  16.     {  
  17.         //遍历寻找  
  18.         for (int i = 0; i < element->timers->num; ++i)  
  19.         {  
  20.             TimerTargetSelector *timer = static_cast<TimerTargetSelector*>(element->timers->arr[i]);  
  21.             //如果正在执行的Timer是需要被unschedule的timer,将其移除并且标识当前正在执行的Timer需要被移除状态为true。  
  22.             if (selector == timer->getSelector())  
  23.             {  
  24.                 if (timer == element->currentTimer && (! element->currentTimerSalvaged))  
  25.                 {  
  26.                     element->currentTimer->retain();  
  27.                     element->currentTimerSalvaged = true;  
  28.                 }  
  29.                   
  30.                 ccArrayRemoveObjectAtIndex(element->timers, i, true);  
  31.                   
  32.                 // update timerIndex in case we are in tick:, looping over the actions  
  33.                 if (element->timerIndex >= i)  
  34.                 {  
  35.                     element->timerIndex--;  
  36.                 }  
  37.                   
  38.                 //当前timers中不再含有timer。但是如果正在执行的target是该target,则将正在执行的target将被清除标识为true  
  39.                 //否则,可以直接将其从hash中移除  
  40.                 if (element->timers->num == 0)  
  41.                 {  
  42.                     if (_currentTarget == element)  
  43.                     {  
  44.                         _currentTargetSalvaged = true;  
  45.                     }  
  46.                     else  
  47.                     {  
  48.                         removeHashElement(element);  
  49.                     }  
  50.                 }  
  51.                   
  52.                 return;  
  53.             }  
  54.         }  
  55.     }  
  56. }  

         同理反观TimerTargetCallback,查找时需要用到std::string,这里不再赘述。
 
 

2.3.2 Scheduler的两种定时模式

Scheduler允许有两种定时模式:
        1.带有interval(间隔)的定时模式,哪怕interval是0.(普通函数)
        2.不带有interval的定时模式,即在每一帧更新之后都会调用到,会将一个类的update函数放入定时器。(此外,模式2还引入了优先级的概念)
        从实现的源代码来看,如果你有一个需要每帧更新都需要调用的function or method,请一定将该部分放入类中的update函数后使用模式2来定时。因为每个模式2绑定了一个hash表能快速存取到,提高性能。上面一小节介绍的是如何添加和删除模式1的定时,下面看一下模式2.
 
[cpp]  view plain copy
 
  1. template <class T>  
  2.   void scheduleUpdate(T *target, int priority, bool paused)  
  3.   {  
  4.       this->schedulePerFrame([target](float dt){  
  5.           target->update(dt);  
  6.       }, target, priority, paused);  
  7.   }  
别问我从哪里来,我tm来自c++11,如果不懂该写法,请自行谷歌c++11 lambda表达式。
 
        具体开始分析SchedulePerFrame,在此之前,要先介绍两个数据结构。
 
[cpp]  view plain copy
 
  1. // A list double-linked list used for "updates with priority"  
  2. typedef struct _listEntry  
  3. {  
  4.     struct _listEntry   *prev, *next;  
  5.     ccSchedulerFunc     callback;  
  6.     void                *target;  
  7.     int                 priority;  
  8.     bool                paused;  
  9.     bool                markedForDeletion; // selector will no longer be called and entry will be removed at end of the next tick  
  10. } tListEntry;  
  11.   
  12. typedef struct _hashUpdateEntry  
  13. {  
  14.     tListEntry          **list;        // Which list does it belong to ?  
  15.     tListEntry          *entry;        // entry in the list  
  16.     void                *target;  
  17.     ccSchedulerFunc     callback;  
  18.     UT_hash_handle      hh;  
  19. } tHashUpdateEntry;  

     tListEntry,是一个双向链表,target是key,markedForDeletion来告诉scheduler是否需要删除他。tHashUpdateEntry是一个哈希表,通过target可以快速查找到相应的tListEntry。可以注意到,HashEntry中有个List,来表示该entry属于哪个list。在scheduler中,一共有三个updateList,根据优先级分为negativeList,0List,positiveList,值越小越先执行。
 
     数据结构介绍完毕,可以开始介绍函数了。
[cpp]  view plain copy
 
  1. void Scheduler::schedulePerFrame(const ccSchedulerFunc& callback, void *target, int priority, bool paused)  
  2. {  
  3.     //先检查hash中是否存在该target,如果存在,则将其deleteion的标识 置为false后返回。(可能某个操作将其置为true,并且  
  4.     //scheduler还没来得及删除,所以这里只需要再改为false即可)  
  5.     tHashUpdateEntry *hashElement = nullptr;  
  6.     HASH_FIND_PTR(_hashForUpdates, &target, hashElement);  
  7.     if (hashElement)  
  8.     {  
  9. #if COCOS2D_DEBUG >= 1  
  10.         CCASSERT(hashElement->entry->markedForDeletion,"");  
  11. #endif  
  12.         // TODO: check if priority has changed!  
  13.   
  14.         hashElement->entry->markedForDeletion = false;  
  15.         return;  
  16.     }  
  17.   
  18.     // most of the updates are going to be 0, that's way there  
  19.     // is an special list for updates with priority 0  
  20.     //英文注释解释了为啥有一个0List。  
  21.     if (priority == 0)  
  22.     {  
  23.         appendIn(&_updates0List, callback, target, paused);  
  24.     }  
  25.     else if (priority < 0)  
  26.     {  
  27.         priorityIn(&_updatesNegList, callback, target, priority, paused);  
  28.     }  
  29.     else  
  30.     {  
  31.         // priority > 0  
  32.         priorityIn(&_updatesPosList, callback, target, priority, paused);  
  33.     }  
  34. }  
 
[cpp]  view plain copy
 
  1. void Scheduler::appendIn(_listEntry **list, const ccSchedulerFunc& callback, void *target, bool paused)  
  2. {  
  3.     //为该target新建一个listEntry  
  4.     tListEntry *listElement = new tListEntry();  
  5.   
  6.     listElement->callback = callback;  
  7.     listElement->target = target;  
  8.     listElement->paused = paused;  
  9.     listElement->markedForDeletion = false;  
  10.   
  11.     DL_APPEND(*list, listElement);  
  12.   
  13.     // update hash entry for quicker access  
  14.     //并且为该target建立一个快速存取的target  
  15.     tHashUpdateEntry *hashElement = (tHashUpdateEntry *)calloc(sizeof(*hashElement), 1);  
  16.     hashElement->target = target;  
  17.     hashElement->list = list;  
  18.     hashElement->entry = listElement;  
  19.     HASH_ADD_PTR(_hashForUpdates, target, hashElement);  
  20. }  
 
[cpp]  view plain copy
 
  1. void Scheduler::priorityIn(tListEntry **list, const ccSchedulerFunc& callback, void *target, int priority, bool paused)  
  2. {  
  3.     //同理,为target建立一个entry  
  4.     tListEntry *listElement = new tListEntry();  
  5.   
  6.     listElement->callback = callback;  
  7.     listElement->target = target;  
  8.     listElement->priority = priority;  
  9.     listElement->paused = paused;  
  10.     listElement->next = listElement->prev = nullptr;  
  11.     listElement->markedForDeletion = false;  
  12.   
  13.     // empty list ?  
  14.     if (! *list)  
  15.     {  
  16.         DL_APPEND(*list, listElement);  
  17.     }  
  18.     else  
  19.     {  
  20.         bool added = false;  
  21.         //根据优先级,将element放在一个合适的位置,标准的有序链表插入操作,不多解释。  
  22.         for (tListEntry *element = *list; element; element = element->next)  
  23.         {  
  24.             if (priority < element->priority)  
  25.             {  
  26.                 if (element == *list)  
  27.                 {  
  28.                     DL_PREPEND(*list, listElement);  
  29.                 }  
  30.                 else  
  31.                 {  
  32.                     listElement->next = element;  
  33.                     listElement->prev = element->prev;  
  34.   
  35.                     element->prev->next = listElement;  
  36.                     element->prev = listElement;  
  37.                 }  
  38.   
  39.                 added = true;  
  40.                 break;  
  41.             }  
  42.         }  
  43.   
  44.         // Not added? priority has the higher value. Append it.  
  45.         if (! added)  
  46.         {  
  47.             DL_APPEND(*list, listElement);  
  48.         }  
  49.     }  
  50.   
  51.     // update hash entry for quick access  
  52.     tHashUpdateEntry *hashElement = (tHashUpdateEntry *)calloc(sizeof(*hashElement), 1);  
  53.     hashElement->target = target;  
  54.     hashElement->list = list;  
  55.     hashElement->entry = listElement;  
  56.     HASH_ADD_PTR(_hashForUpdates, target, hashElement);  
  57. }  

 
ok,到这里,我们已经明白update的定时是如何添加进来的,scheduler用了下面的成员来管理这些entry。
  
[cpp]  view plain copy
 
  1. //  
  2.    // "updates with priority" stuff  
  3.    //  
  4.    struct _listEntry *_updatesNegList;        // list of priority < 0  
  5.    struct _listEntry *_updates0List;            // list priority == 0  
  6.    struct _listEntry *_updatesPosList;        // list priority > 0  
  7.    struct _hashUpdateEntry *_hashForUpdates; // hash used to fetch quickly the list entries for pause,delete,etc  
 
         下面,继续分析源码,看一下是如何移除这些update的定时的。
 
      
[cpp]  view plain copy
 
  1. void Scheduler::unscheduleUpdate(void *target)  
  2. {  
  3.       
  4.     if (target == nullptr)  
  5.     {  
  6.         return;  
  7.     }  
  8.   
  9.     tHashUpdateEntry *element = nullptr;  
  10.     HASH_FIND_PTR(_hashForUpdates, &target, element);  
  11.     if (element)  
  12.     {  
  13.         if (_updateHashLocked)  
  14.         {  
  15.             element->entry->markedForDeletion = true;  
  16.         }  
  17.         else  
  18.         {  
  19.             this->removeUpdateFromHash(element->entry);  
  20.         }  
  21.     }  
  22. }  
         代码简介易懂,唯一需要注意的地方是当updateHashLocked为true时,表示当前情况下不允许更改该hash表,只能先将其deletion标记为true。(在执行update的时候会将这类定时删除)这样在执行update时,即使其在hash表中,也不会执行(因为deletion为true)。标识updateHashLocked,将在scheduler的update函数开始时置为true,然后在结尾置为false,其他时候不会被更改。update函数会在后面介绍,下面,继续看unschedule的其他方法。
 
        
[cpp]  view plain copy
 
  1. void Scheduler::unscheduleAllForTarget(void *target)  
  2. {  
  3.     // explicit nullptr handling  
  4.     if (target == nullptr)  
  5.     {  
  6.         return;  
  7.     }  
  8.   
  9.     // Custom Selectors  
  10.     tHashTimerEntry *element = nullptr;  
  11.     HASH_FIND_PTR(_hashForTimers, &target, element);  
  12.   
  13.     if (element)  
  14.     {  
  15.         if (ccArrayContainsObject(element->timers, element->currentTimer)  
  16.             && (! element->currentTimerSalvaged))  
  17.         {  
  18.             element->currentTimer->retain();  
  19.             element->currentTimerSalvaged = true;  
  20.         }  
  21.         ccArrayRemoveAllObjects(element->timers);  
  22.   
  23.         if (_currentTarget == element)  
  24.         {  
  25.             _currentTargetSalvaged = true;  
  26.         }  
  27.         else  
  28.         {  
  29.             removeHashElement(element);  
  30.         }  
  31.     }  
  32.   
  33.     // update selector  
  34.     unscheduleUpdate(target);  
  35. }  
         该方法会移除target相关的所有定时,包括update类型的,包括Custom Selector类型的,和其他的一样,需要注意该标志位。
 
         最后提一下unscheduleAllWithMinPriority,他会将custom 类型的定时全部移除,并将priority大于残烛的update类型定时移除。
 

2.3.3 定时器的更新update

[cpp]  view plain copy
 
  1. void Scheduler::update(float dt)  
  2. {  
  3.     _updateHashLocked = true;  
  4.   
  5.     //timeScale是什么意思呢,正常的速度是1.0,如果你想二倍速放就设置成2.0,如果你想慢慢放,就设置成0.5.  
  6.     if (_timeScale != 1.0f)  
  7.     {  
  8.         dt *= _timeScale;  
  9.     }  
  10.   
  11.     //  
  12.     // Selector callbacks  
  13.     //  
  14.   
  15.     // Iterate over all the Updates' selectors  
  16.     tListEntry *entry, *tmp;  
  17.   
  18.     //首先处理update类型的定时,你可以发现想调用它的callback,必须满足markedForDeletion为false,从而证明我上面的说法。  
  19.     // updates with priority < 0  
  20.     DL_FOREACH_SAFE(_updatesNegList, entry, tmp)  
  21.     {  
  22.         if ((! entry->paused) && (! entry->markedForDeletion))  
  23.         {  
  24.             entry->callback(dt);  
  25.         }  
  26.     }  
  27.   
  28.     // updates with priority == 0  
  29.     DL_FOREACH_SAFE(_updates0List, entry, tmp)  
  30.     {  
  31.         if ((! entry->paused) && (! entry->markedForDeletion))  
  32.         {  
  33.             entry->callback(dt);  
  34.         }  
  35.     }  
  36.   
  37.     // updates with priority > 0  
  38.     DL_FOREACH_SAFE(_updatesPosList, entry, tmp)  
  39.     {  
  40.         if ((! entry->paused) && (! entry->markedForDeletion))  
  41.         {  
  42.             entry->callback(dt);  
  43.         }  
  44.     }  
  45.   
  46.     //处理custom类型的定时  
  47.     // Iterate over all the custom selectors  
  48.     for (tHashTimerEntry *elt = _hashForTimers; elt != nullptr; )  
  49.     {  
  50.         _currentTarget = elt;  
  51.         _currentTargetSalvaged = false;  
  52.         //没有被暂停,则可以处理  
  53.         if (! _currentTarget->paused)  
  54.         {  
  55.             // The 'timers' array may change while inside this loop  
  56.             //循环内是当前target下的所有Timer  
  57.             for (elt->timerIndex = 0; elt->timerIndex < elt->timers->num; ++(elt->timerIndex))  
  58.             {  
  59.                 elt->currentTimer = (Timer*)(elt->timers->arr[elt->timerIndex]);  
  60.                 elt->currentTimerSalvaged = false;  
  61.   
  62.                 elt->currentTimer->update(dt);  
  63.                 //如果currentTimer的update本身内部,在一定条件下unSchedule了本身,则会改变currentTimerSalvaged的标识信息,  
  64.                 //所以要再次进行判断,这就是循环上面英文注释所述之意  
  65.                 if (elt->currentTimerSalvaged)  
  66.                 {  
  67.                     // The currentTimer told the remove itself. To prevent the timer from  
  68.                     // accidentally deallocating itself before finishing its step, we retained  
  69.                     // it. Now that step is done, it's safe to release it.  
  70.                     elt->currentTimer->release();  
  71.                 }  
  72.   
  73.                 elt->currentTimer = nullptr;  
  74.             }  
  75.         }  
  76.   
  77.         // elt, at this moment, is still valid  
  78.         // so it is safe to ask this here (issue #490)  
  79.         elt = (tHashTimerEntry *)elt->hh.next;  
  80.   
  81.         // only delete currentTarget if no actions were scheduled during the cycle (issue #481)  
  82.         //即使在大循环开始时_currentTargetSalvaged被设置为false,现在的值也可能因为上面该target的各种定时函数调用导致其为true  
  83.         if (_currentTargetSalvaged && _currentTarget->timers->num == 0)  
  84.         {  
  85.             removeHashElement(_currentTarget);  
  86.         }  
  87.     }  
  88.   
  89.     //这些update类型的定时要被删除咯~~  
  90.     // delete all updates that are marked for deletion  
  91.     // updates with priority < 0  
  92.     DL_FOREACH_SAFE(_updatesNegList, entry, tmp)  
  93.     {  
  94.         if (entry->markedForDeletion)  
  95.         {  
  96.             this->removeUpdateFromHash(entry);  
  97.         }  
  98.     }  
  99.   
  100.     // updates with priority == 0  
  101.     DL_FOREACH_SAFE(_updates0List, entry, tmp)  
  102.     {  
  103.         if (entry->markedForDeletion)  
  104.         {  
  105.             this->removeUpdateFromHash(entry);  
  106.         }  
  107.     }  
  108.   
  109.     // updates with priority > 0  
  110.     DL_FOREACH_SAFE(_updatesPosList, entry, tmp)  
  111.     {  
  112.         if (entry->markedForDeletion)  
  113.         {  
  114.             this->removeUpdateFromHash(entry);  
  115.         }  
  116.     }  
  117.   
  118.     _updateHashLocked = false;  
  119.     _currentTarget = nullptr;  
  120.   
  121. }  
        到了最重要的函数了,当你把定时都放入了这些list后,定时器是如何按时调用的呢,答案就在update函数中。

       update函数,最需要注意的点是什么?是在循环内部执行每个target的customer定时函数时候,需要注意很可能改变绑定在该Target下的Customer Timer的状态。所以在每次循环之后,都会判断这些状态位,如果被改变,需要做什么操作。在代码注释中,我已经说明。

2.3.4 状态查询与暂停恢复

        bool isScheduled(const std::string& key, void *target);   &&  bool isScheduled(SEL_SCHEDULE selector, Ref *target);
        可以查询customer类型的定时是否被scheduled。
 
        void pauseTarget(void *target);   &&   void resumeTarget(void *target);
        恢复和暂定target相关的所有定时。就是更改状态而已。。
 

   2.3.5 3.x的新特性

       自从3.x开始,进入了c++11的时代,与此同时,正式引入了多线程编程。本人对多线程了解不多,只能简单点出此函数,具体的用法,烦请各位看官谷歌或者微微一笑吧~
 
     /** calls a function on the cocos2d thread. Useful when you need to call a cocos2d function from another thread.
     This function is thread safe.
     @since v3.0
     */
    void performFunctionInCocosThread( const std::function<void()> &function);
 
 

3.小结

      1.Scheduler与Timer的关系相当DataManager与Data的关系。
      2.Scheduler的两种定时模式,一种是customer selector模式,一种是update 模式。
      3.hash表用来存取对应的timer。
      4.Scheduler的update函数调用了所有Timer的update。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值