cocos2d-x 内存管理

一、C++内存管理

1、内存分配区域

创建对象时,可以选择由三个区域来分配:

- 栈区域分配

处理器的内置指令,效率非常高,但容量有限。由处理器自动分配和释放。

栈用来存放函数的参数值、局部变量值等,值类型是分配在栈里的,比如基本的数据类型int、float等都是分配在栈里。

在执行函数时,函数内局部变量在栈上创建存储单元,函数执行完成之后则被自动释放。

- 堆区域分配

动态内存分配。由我们来分配释放,如果不释放,程序结束时由操作系统回收。

比如以new来创建的对象都是分配在堆里,也就是引用类型是分配在堆里。

我们在开发当中需要合理进行对象的创建和释放。

程序在运行时用malloc或new申请内存,由我们自己负责在何时用free或delete释放内存。

也就是对象的生存期由我们来决定,使用灵活,但也难把握。

-- 静态存储区域分配

静态内存空间在程序整个运行期间内都存在,直到程序退出则释放。

比如我们使用static创建的变量,内存在程序编译时就已分配好,适用于全局变量和静态变量。


2、动态内存分配

使用malloc或new分配内存,使用free或delete释放内存

malloc&free:

#include <iostream>

using namespace std;

class MyObject  
{  
public : 
	MyObject(){
		cout << "call constructor." << endl; 
	}  
	
	~MyObject(){ 
		cout << "call destructor." << endl; 
	}  

	void initialize(){ 
		cout << "call initialization." << endl; 
	}  
	
	void destroy(){ 
		cout << "call destroy." << endl; 
	}  
};  

int main(){

	//申请动态内存  
	MyObject *obj = (MyObject *)malloc(sizeof(MyObject)); 
	obj->initialize();
	
	//TODO
	
	obj->destroy();

	//释放内存
	free(obj);
	//防止野指针,占用的内存虽然被free释放,但obj指针本身并没有被清除,会随机指向造成莫名的问题
	obj = nullptr;

	return 0;
}

new&delete:

int main(){

	//申请动态内存  
	MyObject *obj = new MyObject();

	//TODO

	delete obj;
	obj = nullptr;

	return 0;
}


二、Ref内存管理

1、内存引用计数

Ref类的设计来源于Cocos2d-iphone的CCObject类,是3.x版本的产生,之前的版本里叫CCObject类。

Ref类的内存管理是参考Objective-C手动管理引用计数(Reference Count)而设计的。

每个Ref对象都对应一个内部的计数器,这个计数器跟踪对象的引用次数,称为“引用计数”(Reference Count,简称RC)。

当对象被创建时,引用计数为1,为了操持对象存在,延长对象的生命期,可以调用对象的retain(),retain会使对象的引用计数加1,

当不需要这个对象时可以调用release()使引用计数减1。当对象的引用计数为0时,引擎就会将这个对象内存释放掉。

Ref类的相关函数:

retain() 引用计数加1

release() 引用计数减1

autorelease() 引用计数延迟减1

getReferenceCount() 返回当前的引用计数


2、自动释放池

XmlParser* XmlParser::createWithFile(const char* fileName)

{

   XmlParser* pRet = new XmlParser();


   // 


   return pRet;

}

①行处,如果pRet->release(),就会返回一个内存已经被释放了的对象野指针。

所以在第①行处可以使用pRet->autorelease(),qutorelease()是将对象放入自动释放池里,对象的引用计数不会马上减1,而是等到一个消息循环结束后减1,

如果引用计数为0,即,没有被其它类或Ref对象retain,则释放对象,引用计数大于0是不会被释放的。


3、消息循环

消息循环是游戏循环的一个工作职责,说到底还是游戏循环。

消息循环是接收事件,并处理事件。自动释放池的生命周期也是由消息循环管理的。

自动释放池的生命周期,如图:



图中“圈圈”是消息循环周期,它的一个工作职责是维护自动释放池创建和销毁。每次为了处理新的事件,cocos2d-x引擎都会创建一个新的自动释放池,事件处理完成后,

就会销毁这个池,池中对象的引用计数会减1,如果这个引用计数减到0,也就是没有被其它类或Ref对象retain,则释放对象,否则这个对象不会释放,

在这次销毁池的过程中“幸存”下来,它被转移到下一个池中继续生存。


实例:

bool HelloWorld::init()

{

      ......

      this->list = __Array::createWithCapacity(MAX_COUNT);

      this->list->retain();

      ......

}

......
HelloWorld::~HelloWorld()

{

      this->list->removeAllObjects();

      CC_SAFE_RELEASE_NULL(this->list);

}


4、Ref内存管理规则

A、在使用Node节点对象时,addChild函数可以保持Node节点对象,使其引用计数加1。通过removeChild函数移除Node节点对象,并使引用计数减1。

它们都是隐式调用的,由引擎底层封装处理,我们不需要关心内存的管理。

B、如果是__Array和__Dictionary等容器对象,可以通过它们的add相关函数添加元素并会使引用计数加1,相反的remove相关函数删除元素时会使引用对象减1。

但前提是__Array和__Dictionary等容器对象本身不会被释放。

C、如果不属于上面提到的Ref对象,需要保持引用计数,可以显式的调用retain函数使引用计数加1,然后再显式调用release或autorelease函数使引用计数减1。

D、每个retain函数一定要对应一个release或autorelease函数。

E、release函数使对象的引用计数马上减1,这是所谓的“斩立决”,但是是否真的释放掉了内存要看它的引用计数是否为0。autorelease函数只是在对象上做一个标记,

等到消息循环结束时再减1,这是所谓的“秋后问斩”,在“秋天”没有到来之前,它的内存会一直存在,可以安全使用,但“问斩”之后,是否真的释放掉了内存也要看它的引用

计数是否为0。因此无论是哪种方法,引用计数是为0才是释放对象内存的前提条件。


三、Ref内存管理设计模式

1、使用静态构造函数

通过前面的学习,我们在创建Ref对象时,基本上都采用引擎提供的create开头的静态函数来创建的,如下代码:

auto sprite = Sprite::create("xxx.png");

auto menu = Menu::create(item1, NULL);

this->list = __Array::createWithCapacity(MAX_COUNT);

Cocos2d-x官方将这种create开头的静态创建对象的函数,叫做“静态构造函数”。create开头的静态构造函数是采用静态工厂设计模式,工厂设计模式是一种创建模式,

工厂设计模式能够将创建对象的细节屏蔽掉,用户不需要关心它的内部运行机理。在内存管理方面,采用这种工厂模式后,我们不关心对象内存retain、release和autorelease等问题。事实上确实如此,在使用静态构造函数创建了对象,在使用过程中一般不需要retain、release和autorelease的,但如果我们一旦使用了这些语句反而会造成对象内存的异常。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值