Cocos2d-x 引用计数

cocos2d::Ref是所有类的基类,保存了引用计数值。构造函数里将计数值设置为1(为什么是1呢?后面会解释)。retain函数增加计数,release函数减少计数并检查计数值,如果计数值减为0,则delete this

还有一个autorelease函数只是将自身放入全局的对象池中:

继承自Ref的类都有一个静态函数create,比如Node::create()

对于自定义的类型,有个宏CREATE_FUNC时专门用来声明对应类的create函数:

比如HelloWorld类就添加了这个宏来声明函数

可以看到这个create函数在创建对象并调用init函数初始化之后还调用了autorelease。于是每个通过create函数实例化出来的对象都会先放在全局的对象池之中,但这个对象池并不会一直保存引用。

main.cpp中,Application::Run()的主循环里调用了Director::mainLoop(),而这个函数又在最后调用了AutoreleasePool::clear()

AutoreleasePool::clear就是在遍历调用池中对象的release函数并清空当前帧持有的对象:

因此可以认为每个通过create函数创建出来的对象,如果没有手动release或者delete,那么它的生命周期至少是在这帧结束之后。

如果需要持有对象的引用防止发生析构,就需要手动调用retain来增加引用计数。这样当这帧结束之后就不会因为池子的清空而释放内存。值得注意的是retainrelease必须成对调用,否则就会出错。

为了方便管理引用计数,cocos2d::Vector类在对std::vector进行封装的同时,还在pushBack函数和popBack函数中分别调用了retainrelease。于是使用类似的容器时不需要再手动调用即可管理计数。

AutoreleasePool类中使用的还是std::vector,所以加入到池子中的时候并不会增加计数。又因为对象刚创建出来的时候有且仅有池子持有引用,计数值应该是1,所以Ref的构造函数里把引用计数设为了1而不是0

相对于std::shared_ptr而言,这种内存管理方式更加原始而高效。显式地手动调用retainrelease可以避免不经意之间发生循环引用的问题。但是缺点在于没有用外部对象来管理指针,无法判断指针是否还有效,可能访问的是野指针。