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
来增加引用计数。这样当这帧结束之后就不会因为池子的清空而释放内存。值得注意的是retain
和release
必须成对调用,否则就会出错。
为了方便管理引用计数,cocos2d::Vector
类在对std::vector
进行封装的同时,还在pushBack
函数和popBack
函数中分别调用了retain
和release
。于是使用类似的容器时不需要再手动调用即可管理计数。
AutoreleasePool
类中使用的还是std::vector
,所以加入到池子中的时候并不会增加计数。又因为对象刚创建出来的时候有且仅有池子持有引用,计数值应该是1,所以Ref
的构造函数里把引用计数设为了1而不是0
相对于std::shared_ptr
而言,这种内存管理方式更加原始而高效。显式地手动调用retain
和release
可以避免不经意之间发生循环引用的问题。但是缺点在于没有用外部对象来管理指针,无法判断指针是否还有效,可能访问的是野指针。