Cocos2dx游戏开发

1、cocos2d-x学习笔记——内存管理

2016-04-21  本文已影响65人  鱼鸟fly

内存管理主要有两种方式,一种是引用计数,还有一种是垃圾回收机制,而cocos2d-x使用的是前者,接下来解释一下引用计数的原理。

其原理就是在有指针需要引用这个对象的时候将引用计数加1,不需要的时候要减1。当引用计数为0的时候释放内存。

来看看Node的父类,类Ref。这个类有构造函数、析构函数和四个成员函数,retain()、release()、autorelease()和getReferenceCount(),还有一个成员变量_referenceCount。其他的成员变量和引用计数无关,这里不做讨论。下面来解释这四个成员函数和一个成员变量。

_referenceCount,用来存储引用的次数。构造函数在创建类的时候将_referenceCount初始化为1。

retain(),这个函数用于将引用计数加1。如果需要引用这块内存的时候没有加1,则会在使用的时候,可能会遇到使用的这块内存被释放的问题。

release(),这个函数用于将引用计数减1,当引用计数为0的时候释放这快内存。如果retian后没有调用release,引用计数不会为0,内存不会被释放,造成内存泄漏。

autorelease(),这个函数将对象放进自动释放池,自动释放池在每次mainLoop()函数的最后会调用clear()将自动释放池里面所有对象的引用计数都减1。这个函数使用在需要延时调用release()函数的指针上(如这块内存地址需要返回,最常见在create函数里面)。

只要记住这样一条法则就可以了:创建对象后或者调用了retian()一定要调用release(),如果要延迟释放的时候调用autorelease()。

使用引用计数的时候要注意一点,循环引用。其代码如下
A.h

#include <stdio.h>

class B;

class A:public cocos2d::Ref {
   
public:
    
    A();
    ~A();
    
    void setB(B * b);
    
private:
    
    B * _b;
    
};

A.cpp

#include "A.hpp"
#include "B.hpp"

A::A():_b(nullptr)
{
    
}

A::~A()
{
    _b->release();
}

void A::setB(B *b)
{
    CC_SAFE_RELEASE(_b);
    _b = b;
    CC_SAFE_RETAIN(_b);
}

B.h

#include <stdio.h>
#include "A.hpp"

class B:public cocos2d::Ref {
    
    
public:
    
    B();
    ~B();
    
    void setA(A * a);
    
private:
    
    A * _a;
    
};

B.cpp

B::B():_a(nullptr)
{
    
}

B::~B()
{
    _a->release();
}

void B::setA(A *a)
{
    CC_SAFE_RELEASE(_a);
    _a = a;
    CC_SAFE_RETAIN(_a);
}

使用时的代码

A * a = new (std::nothrow)A();
B * b = new (std::nothrow)B();
a->setB(b);
b->setA(a);
a->release();
b->release();

在类A里面有个类型为B成员变量_b,在setB(B *b)函数中对_b调用了retain()函数,在析构函数中对_b调用了release()函数。在类B里面有个类型为A的成员变量_a,在setA(A *a)函数中对_a调用了retain()函数。现在创建类A的实例a,类B的实例b,并且a->setB(b);b->setA(a),最后分别对a、b调用一次release()。这样就会造成循环引用,因为a的引用计数为1,b的引用计数也为1,a和b都没有被释放,也不会调用析构函数里面的release()释放a和b。从而造成内存泄漏。这里提供一种方法,根据程序需要将其中一个set函数里面的retain()去掉,也就是弱引用(weak reference)。

另外,在cocos2d自定义的Vector和Map两个容器类里面,只要是有数据插入就会调用retain函数,有数据删除就会调用release函数。因此,在每次调用create工厂方法后都要将返回值放入到父节点里面或者调用retain方法,不然在下一次调用mainLoop函数的时候就会使用已经释放的内存。

上一篇下一篇

猜你喜欢

热点阅读