Cocos2d-x 源代码分析 : Scheduler(定时器) 源代码分析
源代码版本号 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; }
update函数,最须要注意的点是什么?是在循环内部运行每一个target的customer定时函数时候。须要注意非常可能改变绑定在该Target下的Customer Timer的状态。
所以在每次循环之后,都会推断这些状态位,假设被改变,须要做什么操作。
在代码凝视中,我已经说明。
2.3.4 状态查询与暂停恢复
就是更改状态而已。。
2.3.5 3.x的新特性
This function is thread safe.
@since v3.0
*/
void performFunctionInCocosThread( const std::function<void()> &function);
3.小结
Cocos2d-x 源代码分析 : Scheduler(定时器) 源代码分析的更多相关文章
- 分析 JUnit 框架源代码
本文转载至http://www.ibm.com/developerworks/cn/java/j-lo-junit-src/ 分析 JUnit 框架源代码 理解 JUnit 测试框架实现原理和设计模式 ...
- 【Java收集的源代码分析】Hashtable源代码分析
Hashtable简单介绍 Hashtable相同是基于哈希表实现的,相同每一个元素是一个key-value对,其内部也是通过单链表解决冲突问题,容量不足(超过了阀值)时.相同会自己主动增长. Has ...
- JavaScript定时器原理分析
.header { cursor: pointer } p { margin: 3px 6px } th { background: lightblue; width: 20% } table { t ...
- 0k6410定时器详细分析
看到一篇很好的博文,分析2410定时器中断的使用的,很详细,和大家分享一下 转载来源于http://www.cnblogs.com/Neddy/archive/2011/07/01/2095176.h ...
- scheduler源码分析——preempt抢占
前言 之前探讨scheduler的调度流程时,提及过preempt抢占机制,它发生在预选调度失败的时候,当时由于篇幅限制就没有展开细说. 回顾一下抢占流程的主要逻辑在DefaultPreemption ...
- nodejs的Express框架源码分析、工作流程分析
nodejs的Express框架源码分析.工作流程分析 1.Express的编写流程 2.Express关键api的使用及其作用分析 app.use(middleware); connect pack ...
- 一、SDWebImage分析--库处理流程分析
二.SDWebImage分析--源码具体分析 这阵子看了SDWebImage的实现跟源代码.也看了下网上的一些总结. 这里我自己画了个流程图来辅助理解下SDWebImage这个库的实现流程.相信也是有 ...
- SVN-项目 XXX 受源代码管理。向源代码管理注册此项目时出错。建议不要对此项目进行任何修改
错误描述: 项目 XXX 受源代码管理.向源代码管理注册此项目时出错.建议不要对此项目进行任何修改 解决办法: 使用记事本打开,项目csproj文件删除图中几行,重新打开解决方案就可以了 原因分析: ...
- 【Java集合源代码剖析】HashMap源代码剖析
转载请注明出处:http://blog.csdn.net/ns_code/article/details/36034955 您好,我正在參加CSDN博文大赛,假设您喜欢我的文章.希望您能帮我投一票.谢 ...
- 手机自动化测试:Appium源码分析之跟踪代码分析九
手机自动化测试:Appium源码分析之跟踪代码分析九 poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家 ...
随机推荐
- 〖Android〗sshd for android, 及映射根文件系统至本地盘符
严重问题: 若移植失败将可能直接导致手机***无法开机***,导入相关文件需慎重! 达成效果: 1. ssh 远程登录 Android 终端: 2. sftp 挂载/映射 Android 根文件系统至 ...
- mybatis中模糊查询的使用以及一些细节问题的注意事项
页面有个功能 为 根据 品牌名进行 关键字查询,对应到数据库的是brand表的name字段的模糊查询 如果用的是SSM框架,在mybatis中我们需要自己写sql语句,涉及到like的模糊查询,myb ...
- Redis C客户端Hiredis代码分析
初始化 redisContext - Redis连接的上下文 /* Context for a connection to Redis */ typedef struct redisContext { ...
- 基于springboot跟poi封装的最便捷的excel导出
发布时间:2018-11-15 技术:springboot1.5.6 + maven3.0.5 + jdk1.8 概述 Springboot最便捷的Excel导出,只需要一个配置文件即可搞定 ...
- ios 中UIViewController的分类
#import <UIKit/UIKit.h> #define TOPVIEWTAG 0x10000 // 导航栏的图片 @interface UIViewController (Chnb ...
- java.lang.IllegalArgumentException: Minimum column number is 0
easyUI的datagrid导出Excel时报如下错误: [2018-06-20 15:00:21] [ERROR] [org.jeecgframework.poi.excel.export.Exc ...
- hihocoder第212周-动态规划
题目链接 import java.util.Scanner; public class Main { long mod = (long) (1e9 + 7); int MAXN = 107; int ...
- 最新的 CocoaPods 的使用教程(一)
发布开源库到CocoaPods的时候.对CocoaPods重新学习了一下. 1.CocoaPods的日常使用 2.创建CocoaPods的私有库 3.创建CocoaPods的开源库 一. CocoaP ...
- 什么是分表和分区 MySql数据库分区和分表方法
1.为什么要分表和分区 日常开发中我们经常会遇到大表的情况,所谓的大表是指存储了百万级乃至千万级条记录的表.这样的表过于庞大,导致数据库在查询和插入的时候耗时太长,性能低下,如果涉及联合查询的情况,性 ...
- 【java】switch case支持的6种数据类型
switch表达式后面的数据类型只能是byte,short,char,int四种整形类型,枚举类型和java.lang.String类型(从java 7才允许),不能是boolean类型. 在网上看到 ...