我们知道,我们在新建一个节点类的时候,一般会在这个类里面添加一个宏,那就是CREATE_FUNC这样的一个宏,
如下:
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__
#include "cocos2d.h"
using namespace cocos2d;
class StoryScene : public cocos2d::Layer
{
public:
static cocos2d::Scene* createScene();
virtual bool init();
CREATE_FUNC(StoryScene);
private:
void menuCallBack();
};
#endif
有些人在添加了这个宏之后,使用这个类去创建对象的时候,会发现这个类有一个create方法,可以创建这个类的对象,并且会自动调用类的init方法去初始化对象,如下:
#include "StoryScene.h"
USING_NS_CC;
Scene* StoryScene::createScene()
{
// 'scene' is an autorelease object
auto scene = Scene::create();
// 'layer' is an autorelease object
auto layer = StoryScene::create();
// add layer as a child to scene
scene->addChild(layer);
// return the scene
return scene;
}
// on "init" you need to initialize your instance
bool StoryScene::init()
{
//////////////////////////////
// 1. super init first
if ( !Layer::init() )
{
return false;
}
return true;
}
其实,这个create方法就是CREATE_FUNC宏帮我们创建的,我们看一下CREATE_FUNC做了什么
/**
* define a create function for a specific type,such as Layer
* @param \__TYPE__ class type to add create(),255)">*/
#define CREATE_FUNC(__TYPE__) \
定义一个静态方法create,返回传入的类的指针
static __TYPE__* create() \
{ \
通过new创建改类的对象
__TYPE__ *pRet = new(std::nothrow) __TYPE__(); \
if (pRet && pRet->init()) \
{ \
将对象加入到自动释放池中
pRet->autorelease(); \
返回该对象
return pRet; \
} \
否则,创建对象失败
else \
{ \
释放对象
delete pRet; \
将对象置空
pRet = NULL; \
返回NULL
return NULL; \
} \
}
其中的autorelease是将对象加入到自动释放池,那这样做的作用是什么,有什么好处吗?
我们以下面的例子为例来将它的作用:
Scene* MenuScene::createScene(int openLevels)
{
auto scene=Scene::create();
//new之后对象的引用计数为1
auto layer=new MenuScene();
if(layer&&layer->init(openLevels))
{
//把对象加入到自动释放池里面,1帧之后对象的引用计数将会减1,引用计数为0的时候对象会被释放
layer->autorelease();
/*layer被添加到其父节点scene上,layer在addChild方法内部会被retain一次,所以此时layer的引用计数为2当一帧过后,layer引用计数将会减1,则1帧后layer的引用计数为2-1=1,当layer被移除父节点的时候,也就是调用layer->removeFromParent()方法的时候,方法内部会调用layer的release方法释放对象,此时的引用计为1-1=0,此时对象被释放从上面我们可以知道,对象的autorelease方法是针对于new的,该方法使我们出对象之后不需要我们手动的去调用对象的release方法去释放对象,减少了内存泄露的几率 用这种方法创建出来的对象,如果没有立即加到父节点中,那么一帧过后使用对象的时候,就会出现空指针异常,因为此时对象已经被释放了*/
scene->addChild(layer);
}else{
CC_SAFE_DELETE(layer);
layer=NULL;
}
return scene;
}
下面是addChild和removeFromParent的底层代码:
void Node::addChild(Node* child,int localZOrder,const std::string &name)
{
CCASSERT(child != nullptr,"Argument must be non-nil");
CCASSERT(child->_parent == nullptr,"child already added. It can't be added again");
addChildHelper(child,localZOrder,INVALID_TAG,name,false);
}
child作为addChildHelper的参数传递了进入,我们跟进addChildHelper看看:
void Node::addChildHelper(Node* child,int tag, const std::string &name, bool setTag)
{
if (_children.empty())
{
this->childrenAlloc();
}
this->insertChild(child,localZOrder);
if (setTag)
child->setTag(tag);
else
child->setName(name);
child->setParent(this);
child->setOrderOfArrival(s_globalOrderOfArrival++);
#if CC_USE_PHYSICS
// Recursive add children with which have physics body.
auto scene = this->getScene();
if (scene && scene->getPhysicsWorld())
{
scene->addChildToPhysicsWorld(child);
}
#endif
if( _running )
{
child->onEnter();
// prevent onEnterTransitionDidFinish to be called twice when a node is added in onEnter
if (_isTransitionFinished)
{
child->onEnterTransitionDidFinish();
}
}
if (_cascadeColorEnabled)
{
updateCascadeColor();
}
if (_cascadeOpacityEnabled)
{
updateCascadeOpacity();
}
}
我们再跟进insertChildvoid Node::insertChild(Node* child,int z)
{
_transformUpdated = true;
_reorderChildDirty = true;
_children.pushBack(child);
child->_localZOrder = z;
}
再跟进pushBack
void pushBack(T object)
{
CCASSERT(object != nullptr,"The object should not be nullptr");
_data.push_back( object );
object->retain();
}
我们可以看到,对象被retain了
同理跟进removeFromParent的源代码,我们也会发现对象被release了。
这就是cocos的自动内存管理机制。