cocos2d-x回调中需注意陷阱

             cocos2d-x回调的代码要规避autorelease对象意外释放的问题,这是昨日调试Android一个平台登录闪退的bug时发现。更准确地说,cocos2d-x的autoreleasepool机制只只适用于单线程。

如下是部分cocos2d-x 2.14源代码 

CCObject* CCObject::autorelease(void)
{
    CCPoolManager::sharedPoolManager()->addObject(this);
    return this;
}
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.
}
void CCPoolManager::addObject(CCObject* pObject)
{
    getCurReleasePool()->addObject(pObject);
}
void CCPoolManager::pop()
{
    if (! m_pCurReleasePool)
    {
        return;
    }

     int nCount = m_pReleasePoolStack->count();


    m_pCurReleasePool->clear();
 
      if(nCount > 1)
      {
        m_pReleasePoolStack->removeObjectAtIndex(nCount-1);

        m_pCurReleasePool = (CCAutoreleasePool*)m_pReleasePoolStack->objectAtIndex(nCount - 2);
    }
}
void CCDisplayLinkDirector::mainLoop(void)
{
    if (m_bPurgeDirecotorInNextLoop)
    {
        m_bPurgeDirecotorInNextLoop = false;
        purgeDirector();
    }
    else if (! m_bInvalid)
     {
         drawScene();
     
         // release the objects
         CCPoolManager::sharedPoolManager()->pop();        
     }
}

mainLoop函数几乎就是cocos2dx一帧循环的主体,显然auorelease实际是将CCObject对象委托给CCPoolManager单例对象,在一帧循环mainLoop收尾处对这些auorelease过的对象调用实际的release函数。咋眼一看所有的reference=1的autorelease过的对象保证在这一帧有效,在一帧最后被release。但是显然多线程就不能保证了,假如在另一个线程hreadB中使用XXX::create产生的对象A,就有hreadB线程内对象A正在使用,但主线程就在这时进入mainLoop的CCPoolManager::sharedPoolManager()->pop()将对象A释放的危险。
当然就这一点一般都能想的到,但Android的Java层通过JNI机制回调native函数时实际上往往回调于一个Handle的消息循环,Handle的消息循环实际上就是另一个线程啊!!!细思恐极啊!!!!这样以来所有JNI机制的native回调全都不安全啊!!!!你甚至不能使用所有cocos2dx里面的对象和函数,因为autorelease充斥于整个cocos2dx的代码,如果你要使用也要在胆颤心惊中查看绝大多数函数和对象的源码。。。。
解决方法

一切都是多线程同步惹得祸,也可以用同步解决。来个萎缩的方法,读源代码可知mainLoop中CCPoolManager::sharedPoolManager()->pop()在这一步释放这一帧所有产生的autorelease对象。那干脆加个线程锁,保证回调时不会释放autorelease对象。
修改源代码如下

struct CCMutex {
	CCMutex(){
		pthread_mutex_init(&mutex_, NULL);
	}
	~CCMutex(){
		pthread_mutex_destroy(&mutex_);
	}
	void Lock(){
		pthread_mutex_lock(&mutex_);
	}
	void unLock(){
		pthread_mutex_unlock(&mutex_);
	}
private:
	pthread_mutex_t mutex_;
};

class CC_DLL CCPoolManager
{
    CCArray*    m_pReleasePoolStack;    
    CCAutoreleasePool*                    m_pCurReleasePool;

    CCAutoreleasePool* getCurReleasePool();
public:
    CCPoolManager();
    ~CCPoolManager();
    void finalize();
    void push();
    void pop();

    void removeObject(CCObject* pObject);
    void addObject(CCObject* pObject);

    static CCPoolManager* sharedPoolManager();
    static void purgePoolManager();

    friend class CCAutoreleasePool;

    CCMutex m_mutex;
};
void CCPoolManager::pop()
{
    m_mutex.Lock();


    if (! m_pCurReleasePool)
    {
    	m_mutex.unLock();
        return;
    }


     int nCount = m_pReleasePoolStack->count();

    m_pCurReleasePool->clear();
 
      if(nCount > 1)
      {
        m_pReleasePoolStack->removeObjectAtIndex(nCount-1);
        m_pCurReleasePool = (CCAutoreleasePool*)m_pReleasePoolStack->objectAtIndex(nCount - 2);
    }

    /*m_pCurReleasePool = NULL;*/
    m_mutex.unLock();
}

jni机制native回调函数同样加入
void Java_com_xxxx_callback(...) { CCPoolManager::sharedPoolManager()->m_mutex.Lock(); //xxxx其他 CCPoolManager::sharedPoolManager()->m_mutex.unLock(); }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值