参考文章:
http://blog.csdn.net/musicvs/article/details/8689345
http://blog.csdn.net/honghaier/article/details/8160519
http://goldlion.blog.51cto.com/4127613/784134
cocos2d-x采用了引用计数机制来进行内存管理。每个对象都有一个用来控制生命周期的引用计数器。在对象通过构造函数创建时,该对象的引用计数值就被赋值为1,表示对象是由创建者所引用。因为如Scene、Layer等类都是继承自Ref类,而C++中,一个类的构造函数总是会先调用父类的构造函数,然后再执行自身代码。而Ref类中的无参构造函数,就是把引用计数值初始化为1。
retain(),令指针所指向的对象的引用计数值加1,表示获取该对象的引用权。
release(),引用结束时,调用该方法使对象的引用计数值减1,表示释放该对象的引用权。
autolease(),其作用就是将对象放入自动回收池。cocos2d-x会保证每一帧结束后释放一次回收池,而调用了autorelease()方法的对象,将会在自动回收池释放的时候被释放一次。但也只是释放一次而已。
注意这样一句话:当引用计数为0时,标志着该对象的生命周期结束,自动触发对象的回收释放。
疑问:怎么自动触发对象的回收释放?
解答:这是因为release()进行的操作是,先把引用计数减1,再判断是否为0,如果为0,就用delete删除对象。
工厂方法
工厂方法,泛指一切生成并返回一个对象的静态函数。
代码1:
static HelloWorldScene* create(){
HelloWorldScene *pRet = new HelloWorldScene();
return pRet;
}
代码2:
static HelloWorldScene* create(){
HelloWorldScene *pRet = new HelloWorldScene();
delete pRet;
return pRet;
}
代码3:
static HelloWorldScene* create(){
HelloWorldScene *pRet = new HelloWorldScene();
pRet->autorelease();
return pRet;
}
代码1,会造成 内存泄露,当然可以在析构函数中delete。但是不知道在其他地方是否还有对该对象的引用,如果析构函数中释放了该资源,那么其他地方如果还有对该对象的引用,那么就会发生错误。
代码2,释放了资源,再返回pRet,此时pRet是一个野指针。
代码3,为了返回pRet对象返回给接收者,需要对它进行一次autorelease操作。autorelease方法,对象直到自动回收池释放之前是不会被真正释放掉的。我们可以有足够的时间来对它进行retain操作以便接管pRet对象的引用权(记得配对使用release)
来看一段代码
Scene* HelloWorld::createScene()
{
auto scene = Scene::create();
auto layer = HelloWorld::create();
log("%u", layer->getReferenceCount()); //引用计数值为1
scene->addChild(layer);
log("%u", layer->getReferenceCount()); //引用计数值为2
return scene;
}
layer指针所指的对象,是通过HelloWorld::create()创建的,create()方法中使用了new和autolease()。
new会调用父类Ref的默认无参构造函数,把引用计数值初始化为1。
此时layer所指对象的引用计数值为1。
scene->addChild(layer),layer被引用,那么引用计数值应该加1,为什么这里不用retain()呢?因为addChild()里已经为我们retain()了一次。而且对应的release()也不用我们考虑了。那么此时的引用计数值自然就为2了。
在整个场景销毁时,会把layer的引用计数值减1,那么2-1=1,引用计数值变为1,而因为layer是autorelease的,及layer是放到自动释放池的,自动释放池的对象在每一帧结束后都会调用一次release,此时计数值为1的对象再进行一次release,计数值就变为0,而且调用了delete释放对象资源。
所以这里就要思考autorelease()的作用,看代码3,试想一下,如果返回的HelloWorldScene对象没有被引用,那么引用计数值为1,且pRet放到自动释放池,那么当前帧结束后就会对自动释放池中的每个对象进行一次release,此时对pRet所指的对象(计数值为1)进行一次release,就会把对象释放掉了。不会造成内存泄露。
在new对象以后,使用autorelease(),就不用考虑何时delete的问题。剩下要做的,就是我们自己手动retain()和release(),对象增加了一次引用,就retain()一下,记得retain()和release()是配对出现的,有retain()就有release(),一般release()可以在析构函数中进行。
另外,autorelease只会在下一帧减1,而不是每一帧都减1。