cocos定时器分析

来源:互联网 发布:淘宝联盟高佣活动在哪 编辑:程序博客网 时间:2024/06/05 20:22

注: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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 孩子不愿意学琴怎么办 不喜欢吃水果蔬菜怎么办 小孩不喜欢吃水果怎么办 校长想整老师怎么办 和老师吵架了怎么办 孩子初中转学学籍怎么办 孩子上初中学籍怎么办 18孩子不想上学怎么办 宝宝不爱吃蔬菜怎么办 孩子总丢东西怎么办 孩子经常丢东西怎么办 孩子总是丢东西怎么办 孩子负能量太多怎么办 不要孩子老了怎么办 父母都内向孩子怎么办 孩子不喜欢读课外书怎么办 学习学不进去怎么办 小孩停不下来怎么办 小孩老爱玩不爱学习怎么办 小孩子不喜欢吃菜怎么办 孩子初中不爱学习怎么办 大学不爱学家长怎么办 小孩子不爱读书不听话怎么办 小孩不爱读书写字怎么办 一年级学生不爱学习怎么办 孩子练字怕累怎么办 孩子不愿意学英语怎么办 生了儿子 不喜欢 怎么办 养两个儿子的怎么办 看诗词记不住怎么办 经常读书记不住怎么办 孩子不爱记数字怎么办 考研学不进去怎么办 读了职高后悔怎么办 不喜欢看书的人怎么办 生的儿子不喜欢怎么办 孩子上网不回家怎么办 儿子不想读书了怎么办 中考体育考不好怎么办 小孩突然没礼貌怎么办 小孩读书记忆差怎么办