cocos2dx 之内存管理



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 plaincopyprint?

  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 plaincopyprint?

  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 之内存管理,布布扣,bubuko.com

时间: 2024-10-20 20:55:39

cocos2dx 之内存管理的相关文章

cocos2dx的内存管理机制

首先我们必须说一下c++中变量的内存空间的分配问题,我们在c++中写一个类,可以在栈上分配内存空间也可以使用new在堆上分配内存空间,如果类对象是在栈上分配的内存空间,这个内存空间的管理就不是我们的事了,但如果是在堆上分配的内存空间,当然需要我们来手动的delete了!cocos2dx采用的是在堆上分配内存空间,想想看你在写程序的时候对于cocos2dx中的类是不是大多数都是通过工厂方法获得的一个指针,你见过在栈上分配内存空间的情况吗?所以问题来了,既然在堆上分配内存空间,那么如何管理这个内存空

浅谈自己对cocos2dx的内存管理的理解

拿个自己写Layer的例子 1 auto genMenuWnd = GeneralMenuWnd::create(); 2 CC_BREAK_IF(!genMenuWnd); 3 addChild(genMenuWnd, 100); 该引用计数变化流程 GeneralMenuWnd在create()的时候,其_referenceCount 为1,并在create()的时候autorelease(); addChild()的时候,其_referenceCount变为2: 当该帧执行完后,_refe

菜鸟学习Cocos2d-x 3.x——内存管理

菜鸟学习Cocos2d-x 3.x——内存管理 2014-12-10 分类:Cocos2d-x / 游戏开发 阅读(394) 评论(6) 亘古不变的东西 到现在,内存已经非常便宜,但是也不是可以无限大的让你去使用,特别是在移动端,那么点内存,那么多 APP要抢着用,搞不好,你占的内存太多了,系统直接干掉你的APP,所以说了,我们又要老生常谈了——内存管理.总结COM开发的时候,分析过COM的 内存管理模式:总结Lua的时候,也分析了Lua的内存回收机制:前几天,还专门写了C++中的智能指针在内存

cocos2dx 内存管理

cocos2dx的内存管理移植自Objective-C, 对于没有接触过OC的C++开发人员来说是挺迷惑的.不深入理解内存管理是无法写出好的C++程序的,我用OC和cocos2dx也有一段时间了,在此总结一下,希望对想用cocos2dx开发游戏的朋友有所帮助. C++的动态内存管理一般建议遵循谁申请谁释放的原则,即谁通过new操作符创建了对象,谁就负责通过delete来释放对象.如果对象的生命周期在一个函数内,这很容易做到,在函数返回前delete就行了.但一般我们在函数中new出来的对象的生命

记录关于cocos2dx内存管理机制可能崩溃的一个坑

大年初一写代码,纪念一下:) cocos2dx,内存管理方式如下: 所有Ref继承而来的类,皆放入AutoreleasePool,每一帧释放一次,如果引用计数为0,则delete. 因此,Ref在每帧的工作,就是先ref+1,后ref-1,让它ref-1的是AutoreleasePool,而让它ref+1的就各有方式了.最常见的就是CCNode,每帧为子节点retain的方式. 一般这是不会有问题的.如果你有需求在类中保存一个Ref对象,而它并非一个子节点,如RenderTexture,则必须记

cocos2dx内存管理

cocos2dx基于引用计数管理内存,所有继承自CCObject的对象都将获得引用计数的能力,可通过调用retain成员函数用于引用计数值,调用release减少引用计数值,当计数值减为0时销毁对象. cocos2dx的对象管理是树形结构的,可通过调用父亲节点的addChild成员函数将一个子节点对象添加到父节点中,当子节点被添加到父亲节点中,子节点的引用计数值加1,如果通过removeChild将子节点从父节点中移除子节点的引用计数值减1.当父节点被销毁时,会遍历其所有的子节点,将其子节点的引

【cocos2d-x 3.x 学习笔记】对象内存管理

内存管理 内存管理一直是一个不易处理的问题,开发者必须考虑分配回收的方式和时机,针对堆和栈做不同的优化处理,等等.内存管理的核心是动态分配的对象必须保证在使用完毕后有效地释放内存,即管理对象的生命周期.由于C++是一个较为底层的语言,其设计上不包含任何智能管理内存的机制.一个对象在使用完毕后必须被回收,然而在复杂的程序中,对象所有权在不同程序片段间传递或共享,使得确定回收的时机十分困难,因此内存管理成为了程序员十分头疼的问题. 另一方面,过于零散的对象分配回收可能导致堆中的内存碎片化,降低内存的

cocos2d-x 3.0 内存管理机制

***************************************转载请注明出处:http://blog.csdn.net/lttree******************************************** 再来一弹,内存管理机制 1.简言机制 2.代码观机制 1.简言-Cocos2d-x的内存管理机制 说到内存管理,这是个question,(⊙v⊙)嗯.. 看一下各语言怎么进行内存管理吧? --JAVA: 堆区的就是new来分配内存.通过垃圾回收机制来回收. (详

2、COCOS2D-X内存管理机制

在C++中,动态内存分配是一把双刃剑,一方面,直接访问内存地址提高了应用程序的性能,与使用内存的灵活性:另一方面,由于程序没有正确地分配与释放造成的例如野指针,重复释放,内存泄漏等问题又严重影响着应用程序的稳定性. 人们尝试着不同的方案去避免这个问题,比较常用的如智能指针,自动垃圾回收等,这些要么影响了应用程序的性能,要么仍然需要依赖于开发者注意一些规则,要么给开发者带来了另外一些很丑陋的用法(实际上笔者很不喜欢智能指针).因此,优秀的C++内存管理方案需要兼顾性能,易用性,所以到目前为止C++