cocos2d-x的内存管理

5 篇文章 0 订阅
4 篇文章 0 订阅

内存管理是移动开发的非常重要的部分,控制不好很容易就爆内存。而内存引起的当机,在后面排查的时候是最麻烦的事情。

cocos2d-x的内存管理是模仿oc的引用计数方法。据说3.0在设计时,有讨论到底是用引用计数还是C++的智能指针。目前,cocos2d-x还是使用引用技术方法。

现有的智能内存管理技术,一是引用技术,一是垃圾回收。引用计数的机制是:通过给每个对象维护一个引用计数器,记录该对象当前被引用的次数。当引用计数为0时,表示该对象的生命周期结束,自动触发对象的回收释放。通过引用计数,把生成和回收的事件,转化成使用和使用结束的事件。不过每个程序片段必须负责任地维护引用计数。烦琐的管理对象生命周期还是必不可少。垃圾回收的机制是:引入一种自动的内存回收器,它会自动跟踪每个对象的所有引用,以便找到所有在使用的对象,释放其余不用的对象。垃圾回收器是一个低级别的线程,不能够指定回收某个对象,其回收的时间也不可控。

cocos2d-x使用了引用技术和自动回收的管理机制,非常类似与oc的内存管理机制。

在Object类定义中每个对象都有一个引用计数器。通过retainCount()可以获得对象的引用计数值。通过构造器创建对象,引用值会赋1。其他地方需要引用对象时,需调用retain(),使引用计数+1。在引用结束时,需调用release()方法,使引用计数-1。

class CC_DLL Object
{
public:
    /// object id, ScriptSupport need public _ID
    unsigned int        _ID;
    /// Lua reference id
    int                 _luaID;
protected:
    /// count of references
    unsigned int        _reference;
    /// count of autorelease
    unsigned int        _autoReleaseCount;
public:
    /**
     * Constructor
     *
     * The object's reference count is 1 after construction.
     * @js NA
     */
    Object();
    
    /**
     * @js NA
     * @lua NA
     */
    virtual ~Object();
    
    /**
     * Release the ownership immediately.
     *
     * This decrements the object's reference count.
     *
     * If the reference count reaches 0 after the descrement, this object is
     * destructed.
     *
     * @see retain, autorelease
     * @js NA
     */
    inline void release()
    {
        CCASSERT(_reference > 0, "reference count should greater than 0");
        --_reference;

        if (_reference == 0)
            delete this;
    }

    /**
     * Retains the ownership.
     *
     * This increases the object's reference count.
     *
     * @see release, autorelease
     * @js NA
     */
    inline void retain()
    {
        CCASSERT(_reference > 0, "reference count should greater than 0");
        ++_reference;
    }

    /**
     * Release the ownership sometime soon automatically.
     *
     * This descrements the object's reference count at the end of current
     * autorelease pool block.
     *
     * If the reference count reaches 0 after the descrement, this object is
     * destructed.
     *
     * @returns The object itself.
     *
     * @see AutoreleasePool, retain, release
     * @js NA
     * @lua NA
     */
    Object* autorelease();

    /**
     * Returns a boolean value that indicates whether there is only one
     * reference to the object. That is, whether the reference count is 1.
     *
     * @returns Whether the object's reference count is 1.
     * @js NA
     */
    bool isSingleReference() const;

    /**
     * Returns the object's current reference count.
     *
     * @returns The object's reference count.
     * @js NA
     */
    unsigned int retainCount() const;

    /**
     * Returns a boolean value that indicates whether this object and a given
     * object are equal.
     *
     * @param object    The object to be compared to this object.
     *
     * @returns True if this object and @p object are equal, otherwise false.
     * @js NA
     * @lua NA
     */
    virtual bool isEqual(const Object* object);
    /**
     * @js NA
     * @lua NA
     */
    virtual void acceptVisitor(DataVisitor &visitor);
    /**
     * @js NA
     * @lua NA
     */
    virtual void update(float dt) {CC_UNUSED_PARAM(dt);};
    
    friend class AutoreleasePool;
};

这里的_reference即是引用计数。可以再看看Object的实现。

/****************************************************************************
Copyright (c) 2010-2012 cocos2d-x.org
Copyright (c) 2013-2014 Chukong Technologies

http://www.cocos2d-x.org

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/


#include "CCObject.h"
#include "CCAutoreleasePool.h"
#include "ccMacros.h"
#include "CCScriptSupport.h"

NS_CC_BEGIN

Object::Object()
: _luaID(0)
, _reference(1) // when the object is created, the reference count of it is 1
, _autoReleaseCount(0)
{
    static unsigned int uObjectCount = 0;

    _ID = ++uObjectCount;
}

Object::~Object()
{
    // if the object is managed, we should remove it
    // from pool manager
    if (_autoReleaseCount > 0)
    {
        PoolManager::sharedPoolManager()->removeObject(this);
    }

    // if the object is referenced by Lua engine, remove it
    if (_luaID)
    {
        ScriptEngineManager::getInstance()->getScriptEngine()->removeScriptObjectByObject(this);
    }
    else
    {
        ScriptEngineProtocol* pEngine = ScriptEngineManager::getInstance()->getScriptEngine();
        if (pEngine != NULL && pEngine->getScriptType() == kScriptTypeJavascript)
        {
            pEngine->removeScriptObjectByObject(this);
        }
    }
}

Object* Object::autorelease()
{
    PoolManager::sharedPoolManager()->addObject(this);
    return this;
}

bool Object::isSingleReference() const
{
    return _reference == 1;
}

unsigned int Object::retainCount() const
{
    return _reference;
}

bool Object::isEqual(const Object *object)
{
    return this == object;
}

void Object::acceptVisitor(DataVisitor &visitor)
{
    visitor.visitObject(this);
}

NS_CC_END

可见autorelease()的作用是把对象通过PoolManager放入自动回收池。当回收池自身被释放时,它会对池中的所有对象执行一次release()方法。引擎在每次游戏循环开始前,创建一个回收池,在循环结束后释放回收池。一个游戏循环即是一帧。猜测如果我们使用自动回收池,就需要保证最后给自动回收池的对象的引用数必须保证为1。假设有个对象,进入回收销毁前引用计数是2,通过回收池销毁后,对象的引用计数应该为1。而下次循环对象又不会在进入回收池,那么这个对象将不会被回收池回收。这样猜不知道对不对。


另外,除了自动回收池外,也可以使用PoolManager自己创建回收池,但是要调用push()和pop()来操作回收池的创建和释放。估计是用不上了吧。

retain和release应该是成对出现。如果不放入自动回收池的对象,应自行调用release方法来释放对象。


对象创建的工厂方法,如下这个方法,会造成内存泄露。

Object* factoryCreate()
{
    Object* ret = new Object();
    return ret;
}
因为它的ret没有被release,这个对象将无法被回收。这时,可以用autorelease来处理这个问题。

Object* factoryCreate()
{
    Object* ret = new Object();
    ret->autorelease();
    return ret;
}

下面这段代码,对象传值时需要注意retain和release的顺序

void SomeClass::setObject(Object* other)
{
    this->object->release();
    other->retain();
    this->object = other;
}
正常情况下,这不会有问题。但如果object本来就和other是一个对象,就会造成other指向的对象已被释放。所以可改成这样

void SomeClass::setObject(Object* other)
{
    other->retain();
    this->object->release();
    this->object = other;
}

autorelease虽然能避免一些问题,但也会引发一些很难排查的错误。如对象释放的次数超过了应有的次数,也会产生崩溃。所以,还是应该避免滥用autorelease,并注意维护引用的计数。


cocos2d-x引擎提供了oc风格的容器,如Array、Dictionary。相对使用标准库的vector等,这些容器不用我们去管理内存。

最后,cocos2d-x提供了与内存管理有关的宏

#define CC_SAFE_DELETE(p)           do { delete (p); (p) = nullptr; } while(0)
#define CC_SAFE_DELETE_ARRAY(p)     do { if(p) { delete[] (p); (p) = nullptr; } } while(0)
#define CC_SAFE_FREE(p)             do { if(p) { free(p); (p) = nullptr; } } while(0)
#define CC_SAFE_RELEASE(p)          do { if(p) { (p)->release(); } } while(0)
#define CC_SAFE_RELEASE_NULL(p)     do { if(p) { (p)->release(); (p) = nullptr; } } while(0)
#define CC_SAFE_RETAIN(p)           do { if(p) { (p)->retain(); } } while(0)

内存管理的机制很复杂,即使它采用了引用计数和自动回收,还是很有可能会出现错误。必须小心维护内存,避免内存泄露和崩溃。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值