《我所理解的cocos2d-x》读书笔记

Cocos2d-x 内存管理机制

为什么不用智能指针

1、 智能指针性能损耗较大
shared_ptr为了保证线程安全会使用一定形式的互斥锁保证引用计数正确

2、智能指针需要显式声明
例如

shared_ptr<Node*> node(new Node());
引用计数

Cocos2d-x 中几乎所有对象都继承 Ref 基类。Ref 基类主要职责就是对对象进行引用计数管理

当一个对象使用由 new 运算符分配的内存时,其引用计数为 1,调用 retain()方法会增加其引用计数,调用 release() 方法则会减少其引用计数,release()方法会在引用计数为0时自动调用 delete 运算符删除对象并释放内存。

autorelease()方法声明一个“智能指针”

Cocos2d-x 使用 autorelease()方法声明一个对象指针为智能指针,这些智能指针被加入一个 AutoreleasePool 中,在每一帧结束的时候对加入 AutoreleasePool 中的对象进行清理,即一个智能指针对象的声明周期从被创建开始到帧末结束。
但是这需要手动声明其“智能”

auto node = new Node();
node->autorelease();

cocose2d-x 使用静态 create()返回一个智能指针对象(内部会主动调用autorelease()

AutoreleasePool 队列

对于一些游戏对象而言,一帧的生命周期过长。假设一帧中调用100个方法,每个方法创建10个 autorelease 对象,并且这些对象只在每个方法的作用域内被使用,即该帧末时,内存中有1000个游戏对象。实际上,每帧平均只需要10个游戏对象的内存(即这些方法顺序调用,且调用后回收内存)

Cocos2d-x 通过一个 AutoreleasePool 队列来实现智能指针生命周期的自定义,并由 PoolManager 来管理 AutoreleasePool 队列。

PoolManager 的初始状态默认至少有一个 AutoreleasePool,用来存储 UI 元素对象。AutoreleasePool 在构造函数中将自身指针添加到 PoolManager 的 AutoreleasePool 队列中析构的时候从队列中移除自己并将加入自身的对象调用release()Ref::autorelease()会将自己添加到当前 AutoreleasePool 中,只要当前 AutoreleasePool 始终为队列尾端的元素,声明一个 AutoreleasePool 对象就可以影响之后的对象,直到该 AutoreleasePool 对象移除队列为止

Class MyClass : public Ref
{
	static MyClass* create(){
		auto ref=new MyClass();
		return ref->autorelease();
	}
}

void customAutoreleasePoolP()
{
	AutoreleasePool pool;
	auto ref1=Myclass::create();
	auto ref2=Myclass::create();
}

方法开始执行时,声明一个 AutoreleasePool 类型的自动变量 pool,其构造函数会将自己加入 PoolManager 的 AutoreleasePool 队列的尾端。接下来的 ref1 和 ref2 都会被添加到 pool 池中。方法结束后,自动变量 pool 的生命周期结束,析构函数释放对象,并从队列中移除自己。

Cocos2d-x 中的智能指针

Cocos2d-x 3.1引入了智能指针 RefPtr<T>。其基于RAII实现(Resource Acquisition Is Initialization,动态资源的持有发生在一个对象的生命周期之内,即构造函数中分配内存,析构函数中释放内存)。模仿C++ 11中的 std::shared_ptr ,但是更轻量级,且能结合 Cocos2d-x 内存管理模型,但是无法保证线程安全,因此比 shared_ptr 高效。

特点

RefPtr<T> 需要依赖 Ref 的引用计数来管理内存,所有类型 T 必须是 Ref 类型。
RefPtr 变量和 Ref 指针是强引用关系,所以构造函数会对任何不是 nullptr 的 Ref 指针增加引用计数,除非它是个右值
RefPtr 的赋值操作符被重载,避免转换方法(C++中,只有1个参数的构造函数可以看作一个转换函数)的调用,释放自身旧资源。
RefPtr 重载了 * 操作符,使其能直接访问资源的地址。
RefPtr<T>能直接加入 Vector 和 Map 容器。RefPtr 不仅提供了 T* 到 RefPtr<T> 的转换构造函数,还提供了转到 T* 的转换操作符

inline opertor T* () const {return reinterpret_cast<T*>(_ptr);}

Vector<T> 的 pushBack 方法接收 T 的一个指针,这样该操作符会被自动调用,加入Vector。

和autorelease对比

autorelease依赖自动回收池的释放,各个共享变量几乎无法控制对资源的使用。RefPtr对资源强引用,当对 Node 移除出 UI 树时,还需要使用 reset 释放对 Node 的占用。
因此对UI元素,需要弱引用类型的内存管理方式,只有UI树本身才可以分配和释放内存

缺陷

RefPtr 可以从外部修改引用计数,且弱引用本身也可以修改其引用计数,导致计数回收混乱。

内存管理总结
  • Ref 的引用计数并非线程安全
  • 对于自定义 Node 的子类,添加 create() 方法,并返回一个 autorelease 对象
  • 对自定义的数据类型,如果继承 Ref 且需要动态分配内存,使用 RefPtr 来管理
  • 对只在一个方法内使用的 Ref 对象,需要使用自动回收的,应使用自动变量的 AutoreleasePool 来清理内存
  • 不要动态分配 AutoreleasePool 对象,始终使用自动变量
  • 不要显示调用 RefPtr 的构造函数,因为会导致同时执行构造函数和赋值操作符,造成一次不必要临时指针变量的产生
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值