cocos assertcocos2dx视频教程 downmanagerre更新失败进去不去游戏

cocos2dx基于引用计数管理内存,所有继承自CCObject的对象都将获得引用计数的能力,可通过调用retain成员函数用于引用计数值,调用release减少引用计数值,当计数值减为0时销毁对象.
cocos2dx的对象管理是树形结构的,可通过调用父亲节点的addChild成员函数将一个子节点对象添加到父节点中,当子节点被添加到父亲节点中,子节点的引用计数值加1,如果通过removeChild将子节点从父节点中移除子节点的引用计数值减1.当父节点被销毁时,会遍历其所有的子节点,将其子节点的引用计数值减1,当子节点的计数值为0,销毁子节点.
提到cocos2dx的内存管理,就不得不提autorelease.
我们随便查看一个函数:
CCSprite* CCSprite::createWithTexture(CCTexture2D *pTexture)
CCSprite *pobSprite = new CCSprite();
if (pobSprite && pobSprite-&initWithTexture(pTexture))
pobSprite-&autorelease();
return pobS
CC_SAFE_DELETE(pobSprite);
return NULL;
注意到CCSprite被生成并成功初始化之后(initWithTexture)立刻就调用了autorelease函数.那么autorelease的作用到底是什么.首先当一个对象A被创建出来之后它的引用计数值必定为1,如果我们将A添加到一个父节点B中,此时它的引用计数值为2.之后销毁父节点B,依据上面的描述,父节点被销毁时会将其子节点的引用计数减1.也就是说父节点B被销毁之后A的引用计数值为1。那么我们就必须手动在不再需要A的时候调用release或delete来将A销毁.为了减轻程序员的负担,cocos2dx提供了CCAutoreleasePool当节点A被添加到autoreleasepool之后,我们就不在需要关注A的销毁了.因为引擎会在每帧结束之后清理autoreleasepool中的对象,调用它的release如果如果对象引用计数值降为0将对象销毁.
下面是CCAutoreleasePool::addObject,autorelease最终调用的就是这个函数:
void CCAutoreleasePool::addObject(CCObject* pObject)
m_pManagedObjectArray-&addObject(pObject);
CCAssert(pObject-&m_uReference & 1, "reference count should be greater than 1");
++(pObject-&m_uAutoReleaseCount);
pObject-&release(); // no ref count, in this case autorelease pool added.
向CCAutoreleasePool中添加一个对象,底层管理容器是一个array对象被添加到array的时候不检测重复性,所以个一对象可以被添加多次.添加完成后需要调用release函数减少引用计数,因为在array-&addObject中调用了retain.
应当注意避免对一个object多次调用autorelease,对object调用一次autorelease将导致当前帧结束时对这个对象调用一次release,假设一个对象没被添加进任何容器,则其引用计数值为1,如果调用两次autorelease则当前帧结束时会调用两次release其中第二次必定是在一个已经被销毁的对象上执行的.而如果这一对象已经添加另外一个容器,则会导致那个容器在当前帧结束之后持有一个已经被销毁的对象指针.此问题在cocos2dx rc3中已经添加了assert处理,对象销毁时判断在pool是否有另外的实例存在,如果有则报错.虽然只在debug版本中处理,但也不用通过遍历vector这么低效的手段来判断重复实例的存在吧.难道2.2.3版本的m_uAutoReleaseCount就没有给开发人员提供一点启发?
我们来看看引擎的主循环:
void CCDisplayLinkDirector::mainLoop(void)
if (m_bPurgeDirecotorInNextLoop)
m_bPurgeDirecotorInNextLoop = false;
purgeDirector();
else if (! m_bInvalid)
drawScene();
// release the objects
CCPoolManager::sharedPoolManager()-&pop();
在每帧中调用drawScene(),之后就调用CCPoolManager::sharedPoolManager()-&pop().
void CCPoolManager::pop()
if (! m_pCurReleasePool)
int nCount = m_pReleasePoolStack-&count();
m_pCurReleasePool-&clear();
if(nCount & 1)
m_pReleasePoolStack-&removeObjectAtIndex(nCount-1);
if(nCount & 1)
m_pCurReleasePool = m_pReleasePoolStack-&objectAtIndex(nCount - 2);
m_pCurReleasePool = (CCAutoreleasePool*)m_pReleasePoolStack-&objectAtIndex(nCount - 2);
/*m_pCurReleasePool = NULL;*/
好了关键函数就是m_pCurReleasePool-&clear(),这个函数完成了对象的清理过程.
在这里我们发现了一点奇怪的地方,为啥有个m_pReleasePoolStack还有个m_pCurReleasePool.
首先来看下ReleasePool对象是在哪里被创建的:
void CCPoolManager::push()
//if(!m_pCurReleasePool){
CCAutoreleasePool* pPool = new CCAutoreleasePool();
m_pCurReleasePool = pP
m_pReleasePoolStack-&addObject(pPool);
pPool-&release();
然后我们跟踪程序的运行将断点放在push函数里,断点的第一次命中调用栈如下:
libcocos2d.dll!cocos2d::CCPoolManager::push() 行 145 C++
libcocos2d.dll!cocos2d::CCPoolManager::getCurReleasePool() 行 200
libcocos2d.dll!cocos2d::CCPoolManager::addObject(cocos2d::CCObject * pObject=0x003e9938) 行 189
libcocos2d.dll!cocos2d::CCObject::autorelease() 行 100
libcocos2d.dll!cocos2d::CCDictionary::create() 行 396
libcocos2d.dll!cocos2d::CCConfiguration::init() 行 60
libcocos2d.dll!cocos2d::CCConfiguration::sharedConfiguration() 行 154
libcocos2d.dll!cocos2d::CCDirector::setDefaultValues() 行 199
libcocos2d.dll!cocos2d::CCDirector::init() 行 113
libcocos2d.dll!cocos2d::CCDirector::sharedDirector() 行 97
IslandFight.exe!AppDelegate::applicationWillEnterForeground() 行 143 C++
libcocos2d.dll!cocos2d::CCEGLView::WindowProc(unsigned int message=5, unsigned int wParam=0, long lParam=) 行 444
libcocos2d.dll!cocos2d::_WindowProc(HWND__ * hWnd=0x002a06aa, unsigned int uMsg=5, unsigned int wParam=0, long lParam=) 行 171
第二次调用栈:
libcocos2d.dll!cocos2d::CCPoolManager::push() 行 145 C++
libcocos2d.dll!cocos2d::CCDirector::init() 行 165
libcocos2d.dll!cocos2d::CCDirector::sharedDirector() 行 97
IslandFight.exe!AppDelegate::applicationWillEnterForeground() 行 143 C++
libcocos2d.dll!cocos2d::CCEGLView::WindowProc(unsigned int message=5, unsigned int wParam=0, long lParam=) 行 444
libcocos2d.dll!cocos2d::_WindowProc(HWND__ * hWnd=0x002a06aa, unsigned int uMsg=5, unsigned int wParam=0, long lParam=) 行 171
也就是说总共创建了两个ReleasePool,ReleasePool都被添加到m_pReleasePoolStack成员中,这个成员是一个array而m_pCurReleasePool则指向m_pReleasePoolStack的最后一个成员.
接着我们将断点放到CCPoolManager::pop()中,可以发现第一次pop命中的时候nCount == 2,所以执行if(nCount & 1)之后的流程, 这个流程的处理就是将m_pReleasePoolStack的最后一个成员销毁,然后将m_pCurReleasePool指向m_pReleasePoolStack中最后一个成员.
继续观察程序的运行可以发现,在第一帧之后m_pReleasePoolStack中实际上永远只剩一个成员.我将
bool CCDirector::init(void)中的CCPoolManager::sharedPoolManager()-&push()注释掉,程序依旧能正确的运行.
我搜索cocos2dx 2.2.3和cocos2dx 3.0rc源代码,并无找到必须启用多个ReleasePool的地方, 那么多ReleasePool存在的意义就仅剩调试了.
研究cocos2dx不久,文中有理解不当的地方欢迎指正.
阅读(...) 评论()& & 以前一直打算做这个功能,结果一直没做,最近抽了1天时间研究了下,怕以后又忘记,特意将一些注意事项写来下
& 版本为cocos2dx-3.9,
&目前版本已经废除了以前的AssertManager,使用AssertManagerEx作为资源更新下载器了,功能涵盖了版本控制,版本检查,更新文件,解压,替换。对于刚开始看这个文件的人来说,是比较难搞清楚是怎么用的,经过测试,算是把这东西弄通了,这里需要注意的有以下几点:
1-对于要添加重写的代码,非常少,基本上就是添加一个监听,用来实现下载过程中的事件回调:
& 下载器对象初始化---
#define LOCAL_VERSION_PATH &config/project.manifest&)
bool AssertDownload::init()
& & if (!Layer::init())
& & _loadLayer = Layer::create();
& & addChild(_loadLayer);
std::string storagePath = FileUtils::getInstance()-&getWritablePath();
& & _am = AssetsManagerEx::create(LOCAL_VERSION_PATH, storagePath); &&
//参数1表示本地的project.manifest路径,_am在启动后会读取获取里面的版本号信息,
//参数2 表示本地写入路径,意思是当你下载更新后的文件会存入这个路径里
& & _am-&retain();
/从表面上看,我们找不到任何设置更新服务器地址之类的东西
所以我们先大致上讲下怎么配置;
& 客户端需要在本地resrouce里配1个project.manifes文件 &他的路径就是上面LOCAL_VERSION_PATH,即AssetsManagerEx::create的第1个参数
资源服务器需要配置提供3个:
1:各个备用下载的资源文件的路径文件夹 &我们称这个叫packageUrl ,例如“http://192.168.0.114:9090/update/files/”
2:提供给客户端检查版本用的version.manifest文件地址 &称为remoteVersionUrl 例如“http://192.168.0.114:9090/update/version/version.manifest”
3:提供给客户端检查版本和资源文件版本的project.manifes文件地址,称为remoteManifestUrl 例如“http://192.168.0.114:9090/update/version/project.manifest”
就是说基本上这些配置好了,就没什么代码需要写了,所以这里主要的工作是搞清楚这些配置的作用和用法。
下面我们来详细解释;
所以我们需要打开manifest文件去看内容和一些key
//打开manifest文件,我们发现
& &packageUrl& : &http://192.168.0.114:9090/update/files/&,
& & &remoteVersionUrl& : &http://192.168.0.114:9090/update/version/version.manifest&,
& & &remoteManifestUrl& : &http://192.168.0.114:9090/update/version/project.manifest&,
& & &version& : &1.0.0&,
& & &engineVersion& : &Cocos2d-lua v3.3 Final&,
& & &assets& : {
& & & & &res/Images/sp.png& : {
& & & & & & &md5& : &e6aed39ccc1008040cbc1&
& & & & },
&packageUrl 表示资源服务器的需要下载的文件的网络文件夹,例如下面asserts里面有个res/Images/sp.png
& 假如这个文件需要更新,他的地址就是&packageUrl +&res/Images/sp.png;这样我们就可以拿到这个文件的完整地址,可以直接http获取了。
remoteVersionUrl 表示本地
下面这个函数是按钮回调,启动了下载器,并且添加了个监听器实现监听回调 (cocos2d::extension::AssetsManagerEx* _am)
void AssertDownload::startDownloadCallback(Ref* sender)
log(&start call back&);
& & if (!_am-&getLocalManifest()-&isLoaded())
& & & & CCLOG(&Fail to update assets, step skipped.&);
& & & & onLoadEnd();
& & & & _amListener = cocos2d::extension::EventListenerAssetsManagerEx::create(_am, std::bind(&AssertDownload::handleAssertsEvent,this, std::placeholders::_1));
& & & & Director::getInstance()-&getEventDispatcher()-&addEventListenerWithFixedPriority(_amListener, 1);
& & & & _am-&update();
剩下的一个需要实现的就是handleAssertsEvent函数,里面就是个switch-case
void AssertDownload::handleAssertsEvent(EventAssetsManagerEx* event)
static int failCount = 0;
switch (event-&getEventCode())
case EventAssetsManagerEx::EventCode::ERROR_NO_LOCAL_MANIFEST:
CCLOG(&No local manifest file found, skip assets update.&);
this-&onLoadEnd();
case EventAssetsManagerEx::EventCode::UPDATE_PROGRESSION:
std::string assetId = event-&getAssetId();
float percent = event-&getPercent();
if (assetId == AssetsManagerEx::VERSION_ID)
str = StringUtils::format(&Version file: %.2f&, percent) + &%&;
else if (assetId == AssetsManagerEx::MANIFEST_ID)
str = StringUtils::format(&Manifest file: %.2f&, percent) + &%&;
str = StringUtils::format(&%.2f&, percent) + &%&;
CCLOG(&%.2f Percent&, percent);
if (this-&_progress != nullptr)
this-&_progress-&setString(str);
case EventAssetsManagerEx::EventCode::ERROR_DOWNLOAD_MANIFEST:
case EventAssetsManagerEx::EventCode::ERROR_PARSE_MANIFEST:
CCLOG(&Fail to download manifest file, update skipped.&);
this-&onLoadEnd();
case EventAssetsManagerEx::EventCode::ALREADY_UP_TO_DATE:
case EventAssetsManagerEx::EventCode::UPDATE_FINISHED:
CCLOG(&Update finished. %s&, event-&getMessage().c_str());
this-&onLoadEnd();
case EventAssetsManagerEx::EventCode::UPDATE_FAILED:
CCLOG(&Update failed. %s&, event-&getMessage().c_str());
failCount++;
if (failCount & 5)
_am-&downloadFailedAssets();
CCLOG(&Reach maximum fail count, exit update process&);
failCount = 0;
this-&onLoadEnd();
case EventAssetsManagerEx::EventCode::ERROR_UPDATING:
CCLOG(&Asset %s : %s&, event-&getAssetId().c_str(), event-&getMessage().c_str());
case EventAssetsManagerEx::EventCode::ERROR_DECOMPRESS:
CCLOG(&%s&, event-&getMessage().c_str());
case EventAssetsManagerEx::EventCode::NEW_VERSION_FOUND:
log(&new version found&);
基本上这就是大部分代码了。
2 当然,纯看代码是很难搞懂他的实现和逻辑过程的,接下来,我们分析下这个内部的实现流程和逻辑。
& & & &通过调试跟踪,我们发现,AssetsManagerEx* am 对象创建create的时候,使用了2个参数,第1个参数
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:413次
排名:千里之外VincentTung 的BLOG
用户名:VincentTung
文章数:140
访问量:53310
注册日期:
阅读量:5863
阅读量:12276
阅读量:393711
阅读量:1084806
51CTO推荐博文
1.什么是断言?断言assert 是仅在Debug 版本起作用的宏,它用于检查“不应该”发生的情况。如果是Release版本,编译器忽略所有的断言(看成空语句)。CC_ASSERT(条件)#define CC_ASSERT(cond) assert(cond)CCAsert(条件,"打印信息")#if COCOS2D_DEBUG & 0
extern bool CC_DLL cc_assert_script_compatible(const char *msg);
#define CCAssert(cond, msg) do {
if (!(cond)) {
if (!cc_assert_script_compatible(msg) && strlen(msg)) \
cocos2d::CCLog("Assert failed: %s", msg);
CC_ASSERT(cond);
} while (0)
#define CCAssert(cond, msg) ((void)(cond))
#endif例子://addChild方法,如果要添加的子节点为空,就停住了,并打印信息“Argument must be non-nil”
void CCNode::addChild(CCNode *child)
//不为空不成立,就是为空啦
CCAssert( child != NULL, "Argument must be non-nil");
this-&addChild(child, child-&m_nZOrder, child-&m_nTag);
}2.函数回调,主要有三种方式(无参数,一个参数,二个参数)void call1();
void call2(CCNode* node);
void call3(CCNode* node,void* data);实现:void HelloWorld::call1(){
CCLog("call1");
void HelloWorld::call2(CCNode* node){
CCLog("call2");
void HelloWorld::call3(CCNode* node,void* data){
CCLog("call3");
CCSprite* sp =(CCSprite*)
sp-&setScale(2.0f);
}函数回调:
CCSprite* sp = CCSprite::create("CloseNormal.png");
sp-&setPosition(CCPointZero);
addChild(sp);
CCJumpTo* jump = CCJumpTo::create(1, ccp(400,20), 60, 8);
CCCallFunc* fun1 = CCCallFunc::create(this, callfunc_selector(HelloWorld::call1));
//一个参数
CCCallFunc* fun2 = CCCallFuncN::create(this, callfuncN_selector(HelloWorld::call2));
//两个参数
CCCallFunc* fun3 = CCCallFuncND::create(this, callfuncND_selector(HelloWorld::call3),(void*)sp);
CCSequence* seq = CCSequence::create(jump,fun1,fun2,fun3,0);
sp-&runAction(seq);3.CCProgressTo使用://TestCpp中的例子
CCSprite* sp = CCSprite::create("CloseNormal.png");
sp-&setPosition(CCPointZero);
addChild(sp);
CCSize s = CCDirector::sharedDirector()-&getWinSize();
CCProgressTo *to1 = CCProgressTo::create(2, 100);
CCProgressTimer *left = CCProgressTimer::create(sp);
left-&setType( kCCProgressTimerTypeRadial );
//圆形进度
addChild(left);
left-&setPosition(ccp(100, s.height/2));
left-&runAction( CCRepeatForever::create(to1));哈哈,一天一点进步,加油本文出自 “” 博客,请务必保留此出处
了这篇文章
类别:┆阅读(0)┆评论(0)

我要回帖

更多关于 cocos2dx 教程 的文章

 

随机推荐