cocos2dx的内存管理移植自Objective-C, 对于没有接触过OC的C++开发人员来说是挺迷惑的。不深入理解内存管理是无法写出好的C++程序的,我用OC和cocos2dx也有一段时间了,在此总结一下,希望对想用cocos2dx开发游戏的朋友有所帮助。
C++的动态内存管理一般建议遵循谁申请谁释放的原则,即谁通过new操作符创建了对象,谁就负责通过delete来释放对象。如果对象的生命周期在一个函数内,这很容易做到,在函数返回前delete就行了。但一般我们在函数中new出来的对象的生命周期都会超出该函数体(例如作为函数的返回值),否则我们可以直接在栈上创建对象,不需要使用动态内存,也省去了内存管理的麻烦(当然大对象是不适合在栈上分配的)。如果对象的生命周期超出了创建对象的函数,我们就很难再遵循谁申请谁释放的原则了,我们必须在函数外的某个合适的地方释放对象,如果这个对象同时被多个对象引用,问题就很复杂了,必须保证所有该对象的引用者都不再需要它了才可以释放,在实际使用中这点是很难保证的。于是,各种内存管理技术应运而生了:垃圾回收器,智能指针,引用计数...... cocos2dx移植于Objective-C,因此和OC一样使用了比较原始的引用计数的方法来管理内存。
cocos2dx通过CCObject和CCPoolManager来实现内存管理。所有使用cocos2dx引用计数机制的类都必须派生自CCObject。CCObject有一个计数器成员变量m_uReference,当CCObject被构造时m_uReference=1,表示该对象被引用1次。CCObject的 retain方法可以使计数器加1,release方法可以使计数器减1。当计数器减到0时release方法会通过delete this来销毁自己。
手动内存管理
使用retain和release,我们可以手动管理内存, 通过new 创建的对象,使用release来释放。

[cpp] view plain copy print ?
  1. CCObject *obj=new CCObject();
  2. ...
  3. obj->release();
CCObject *obj=new CCObject();
...
obj->release();

和new\delete需配对使用一样,new\release也要配对使用才可确保内存被释放。有人会说这个把delete换成release有意义吗?需要注意的是这个的release并不等同于delete,release只是表示引用计数减1,并不是真正销毁obj所占用的内存。只有当obj的引用计数为0时内存才会被销毁。下面的代码就展示了release和delete不同:

[cpp] view plain copy print ?
  1. CCArray *array = CCArray::array();
  2. CCObject *obj = new CCObject();// m_uReference=1
  3. array->addObject(obj); // CCArray的addObject方法会自动调用obj的retain方法,使引用计数加1,表示拥有obj,此时m_uReference=2
  4. obj->release(); // 这里的release和new配对,obj引用计数减1,但是并不会释放obj, 此时m_uReference=1;
  5. obj->doSomething(); // 在release之后我们依然可以正常使用obj,它并没有被释放
  6. array->removeObject(obj); //当我们把obj从CCArray中移除时,CCArray会自动调用obj的release,此时m_uReference=0, obj被销毁
  7. obj->doSomething(); // 错误,obj已经被销毁
CCArray *array = CCArray::array();
CCObject *obj = new CCObject();// m_uReference=1
array->addObject(obj); // CCArray的addObject方法会自动调用obj的retain方法,使引用计数加1,表示拥有obj,此时m_uReference=2
obj->release(); // 这里的release和new配对,obj引用计数减1,但是并不会释放obj, 此时m_uReference=1;
obj->doSomething(); // 在release之后我们依然可以正常使用obj,它并没有被释放
array->removeObject(obj); //当我们把obj从CCArray中移除时,CCArray会自动调用obj的release,此时m_uReference=0, obj被销毁
obj->doSomething(); // 错误,obj已经被销毁

对于手动内存管理,我们需遵循new/release,retain/release配对使用的原则,谁new,谁release;谁retain,谁release。new出来的对象如果是要加入到cocos2dx集合中,添加完后一定不要忘记release,集合类已经为你retain了对象,你还是要为你的new配对release一次,否则当这个对象从集合中移除时不会被正确销毁。

自动内存管理
手动内存管理似乎比new/delete更麻烦,而且并没有解决一开始我们提到的函数内创建的对象的生命周期超出函数怎么办的问题。new和release需配对使用,那在函数内创建的对象返回前我们需要调用一次release,在这之前如果我们没有把对象加入到什么集合中,对象就被销毁了,和使用new/delete是一样的。自动内存管理就可以解决这个问题。CCObject有一个autorelease方法,如果一个对象在用new关键字创建之后调用了autorelease,我们就不必关心它的释放问题。CCPoolManager会在游戏的每一帧结束后自动释放这些autorelease的对象。CCPoolManager其实依然是通过引用计数来管理对象生命周期的,它里面有一个CCAutoreleasePool,我们调用CCObject的autorelease就是把自己加入到CCAutoreleasePool的对象数组里面。当每一帧结束的时候,CCPoolManager会将对象从数组中移除,如果这时候对象的引用计数为0,对象就自然被释放了。 对于用new关键字创建之后调用了autorelease的对象,不需要再release一次。
cocos2dx中的大部分对象都可以通过静态工厂方法来创建出这种会自动释放的对象,这是cocos2dx的一条规则,我们自己实现的类最好也遵循这样的规则,以免引起其他开发人员误会。如果一个对象是通过类的静态方法创建而不是new出来的,我们就不需要release它。
其实这里的自动并没有我们想得那么好,对于像C#,Java这种托管语言,虚拟机为你完成了所有内存管理工作,程序员完全从内存分配和释放中解脱了出来。cocos2dx的autorelease只不过每帧结束后自动在为我们释放了一次对象, 如果我们希望创建的对象在下一帧仍然可以使用,我们需要显式地retain一下这个对象或者把对象加入到集合中(集合会帮我们retain一次)。既然retain了,我们还是不能忘记在适当的地方release。比较常见的用法是创建一个autorelease对象作为类成员变量,我们在通过静态方法得到实例的指针后除了赋值给类成员,还要retain一次,然后在类的析构函数中release一次。如果没有retain,以后使用该成员的时候就会因为对象被销毁而发生内存访问错误,这是新手很容易遇到的陷阱。

cocos2dx 采用引用计数管理自己的内存:

引用计数:

引用计数就是通过给每个对象维护一个引用计数器,记录该对象当前被引用的次数。当对象增加一次引用时,计数器+1.而对象失去一次引用时,计数器-1,当引用计数器为0时。标志着该对象的生命周期结束,自动触发对象的回收释放。 为了实现引用计数器,cocos2dx实现了自己的根类ccobject。引 擎中所有类都派生自ccobject类。一下ccobject的定义

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
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;
};

通过以上代码我们可以看出cocos2dx的内存管理机制和objective-c的管理一样。都是当_reference = 0时释放内存。_autoReleaseCount自动释放池的引用计数

?
1
2
3
4
5
6
7
8
9
auto sprite1 = new Sprite();
sprite1->initWithSpriteFrameName( "btn_adventure_normal_CN.png" ); //引用计数器+1
CCLOG( "retaincount = %d" ,sprite1->retainCount());
sprite1->retain(); //引用计数器+1
CCLOG( "retaincount = %d" ,sprite1->retainCount());
sprite1->release(); //引用计数器-1
CCLOG( "retaincount = %d" ,sprite1->retainCount());
sprite1->autorelease(); //只是把该sprite放入自动释放池中。引用计数器不变。等待管理器自动释放
CCLOG( "retaincount = %d" ,sprite1->retainCount());

coco2dx 内存管理原则

1.程序段必须成对执行retain()和release()或者执行autorelease()来开始和结束对对象的引用 2.工厂方法返回前,应通过autorelease()结束对该对象的引用 3.对象传值时,应考虑到新旧对象相同的特殊情况 4.尽量使用release()而不是autorelease()来释放对象,以确保性能。 5.保存ccobject的子类对象时,应严格使用cocos2dx提供的容器。

内存管理涉及到的宏:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#define CC_SYNTHESIZE_RETAIN(varType, varName, funName)    \
private : varType varName; \
public : virtual varType get##funName( void ) const { return varName; } \
public : virtual void set##funName(varType var)   \
{ \
     if (varName != var) \
     { \
         CC_SAFE_RETAIN(var); \
         CC_SAFE_RELEASE(varName); \
         varName = var; \
     } \
}
#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 )

cocos2dx 之内存管理相关推荐

  1. Cocos2Dx之内存管理-欧阳左至

    Cocos2Dx的所有的类都直接或间接继承自CCObject,让类具有自动的内存管理能力.根本上讲,是借鉴自苹果的设计思维.从iPhone版的Cocos2Dx,到C++版的Cocos2Dx,以及到后面 ...

  2. cocos2dx多线程以及线程同步 与 cocos2dx内存管理与多线程问题

    cocos2d-x引擎在内部实现了一个庞大的主循环,每帧之间更新界面,如果耗时的操作放到了主线程中,游戏的界面就会卡,这是不能容忍的,游戏最基本的条件就是流畅性,这就是为什么游戏开发选择C++的原因. ...

  3. Cocos2d-X内存管理研究一

    http://hi.baidu.com/tzkt623/item/651ca7d7a0aff6e055347f67        半夜没事干,研究内核,作为我cocos2d-x的第一篇教程.cocos ...

  4. Cocos2d-x内存管理研究二

    http://hi.baidu.com/tzkt623/item/46a26805adf7e938a3332a04   上一篇我们讲了内核是如何将指针加入管理类进行管理.这次我将分析一下内核是如何自动 ...

  5. cocos2dx多线程以及线程同步 与 cocos2dx内存管理与多线程问题

    cocos2d-x引擎在内部实现了一个庞大的主循环,每帧之间更新界面,如果耗时的操作放到了主线程中,游戏的界面就会卡,这是不能容忍的,游戏最基本的条件就是流畅性,这就是为什么游戏开发选择C++的原因. ...

  6. 跟着石头哥哥学cocos2d-x(三)---2dx引擎中的内存管理模型

    2019独角兽企业重金招聘Python工程师标准>>> 2dx引擎中的对象内存管理模型,很简单就是一个对象池+引用计数,本着学好2dx的好奇心,先这里开走吧,紧接上面两节,首先我们看 ...

  7. 【深入Cocos2d-x】探索Cocos2d-x中的内存管理-引用计数和自动释放池

    2019独角兽企业重金招聘Python工程师标准>>> #深入Cocos2d-x-探索Cocos2d-x中的内存管理-引用计数和自动释放池 ###引用计数(Reference Cou ...

  8. cocos2dx标准容器_cocos2dx[3.2](24)——内存管理机制

    [唠叨] 整合参考文档. [参考] [内存管理机制] 在3.x版本,Cocos2d-x采用全新的根类 Ref,实现Cocos2d-x 类对象的引用计数记录.引擎中的所有类都派生自Ref. 1.引用计数 ...

  9. cocos2d-x学习之自动内存管理

    一.自动内存管理 1)概述 C++语言默认是没有提供自动内存管理的.使用者需要自己分配,自己释放.在cocos2d-x里提供了一个自动内存管理的方案.主要是通过CCObject来提供的,用户只要继承了 ...

最新文章

  1. UNITY录制视屏解决方案 - ShareREC For Unity3D
  2. SpringBatch 写xml文件(StaxEventItemWriter)用法(十四)
  3. git gui管理服务器配置文件,从 Git Gui 管理的Repository(库) 提交更改到 Bonobo服务器管理的Repository(库)...
  4. 一个帮助我100%拿offer的面试学习法
  5. Centos下载地址
  6. 点击高德地图标注没法弹窗_如何在地图上标注我的店铺
  7. 什么不用 iframe 做微前端
  8. Android NDK之JNI陷阱
  9. python怎样安装wordcloud(词云)文件
  10. java插件不启动_java-插件安装后Eclipse启动问题
  11. QT实现简单的浏览器
  12. Qt 读写 txt 文件
  13. ubuntu18.04 安装扫描仪驱动程序
  14. 电子技术基础(三)_第2章放大电路原理__晶体三极管与场效应三极管
  15. 计算机应用基础网课作业答案,知到网课答案计算机应用基础(新)全部答案
  16. 阿里云oss使用cdn,节省oss下行流量
  17. android audiorecord插上耳机录音由dmic切换到耳机录音
  18. windows录屏_Windows及苹果电脑录屏攻略
  19. 如何进行文献检索和阅读(转)
  20. 各省份非金融类对外直接投资存量(2003-2020年)

热门文章

  1. imgaug数据增强神器:增强器一览
  2. 面试时如何回答为何离开老东家
  3. 【组播技术入门 02】组播IP地址及组播MAC地址
  4. 如何将大的pdf文件,压缩至2Mb以内【免费,别想让我花钱!】
  5. Doctype作用? 严格模式与混杂模式如何区分?它们有何意义? 以及如何触发这2种模式?
  6. 5G跌下神坛,相比4G可用的技术较有限,唯一的优势就是速度快一些
  7. 柯杰下赢机器人_柯洁复出再战AI,这次是真正的机器人棋手!
  8. 牛顿法的简单介绍及Matlab实现
  9. 法律网推荐(二) 用Pig进行数据预处理
  10. 【钉钉小程序】报错提示“无跨域调用权限”