bool
HelloWorld::init()
if
(!Layer::init())
{
return
false
;
}
SizevisibleSize=Director::getInstance()->getVisibleSize();
Vec2origin=Director::getInstance()->getVisibleOrigin();
autogoItem=MenuItemImage::create(
"go-down.png"
,
"go-up.png"
CC_CALLBACK_1(HelloWorld::menuCloseCallback,
this
));
goItem->setPosition(Vec2(origin.x+visibleSize.width-goItem->getContentSize().width/2,
origin.y+goItem->getContentSize().height/2));
automenu=Menu::create(goItem,NULL); ①
menu->setPosition(Vec2::ZERO);
->addChild(menu,1); ②
->list=__Array::createWithCapacity(MAX_COUNT);
->list->retain(); ③
for
(
int
i=0;i<MAX_COUNT;++i){
Sprite*sprite=Sprite::create(
"Ball.png"
);
->list->addObject(sprite); ④
}
true
;
}
void
HelloWorld::menuCloseCallback(Ref*pSender)
{
Ref*obj=NULL;
log
(
"list->count()=%d"
->list->count()); ⑤
SizevisibleSize=Director::getInstance()->getVisibleSize();
CCARRAY_FOREACH(
->list,obj){
Sprite*sprite=(Sprite*)obj; ⑥
x=CCRANDOM_0_1()*visibleSize.width;
y=CCRANDOM_0_1()*visibleSize.height;
sprite->setPosition(Vec2(x,y));
->removeChild(sprite);
->addChild(sprite);
}
}
HelloWorld::~HelloWorld()
{
->list->removeAllObjects();
CC_SAFE_RELEASE_NULL(
->list); ⑦
在上述代码中我们需要关注两个对象(Menu和__Array)创建。第①行auto menu = Menu::create(goItem,NULL)通过create静态工厂创建对象,关于静态工厂的创建原理我们会在下一节介绍。如果我们不采用第②行的this->addChild(menu,1)语句将menu 对象放入到当前层(HelloWorld)的子节点列表中,那么这个menu对象就会在当前消息循环结束的时候被释放。调用this->addChild(menu,1)语句会将它的生命周期持续到HelloWorld层释放的时候,而不会在当前消息循环结束释放。
菜单、层等节点对象可以调用addChild函数,使得其生命延续。而且__Array和__Dictionary等Ref对象没有调用addChild函数保持,我们需要显式地调用retain函数保持它们,以便延续其生命。如代码第③行this->list->retain(),list就是一个__Array指针类型的成员变量,如果没有第③行语句,那么在第⑤行代码this->list->count()程序就会出错,因为这个时候list对象已经释放了。采用了retain保持的成员变量,一定要release(或autorelease),retain和release(或autorelease)一定是成对出现的。我们可以在析构函数~HelloWorld()中调用release释放,而第⑦行代码CC_SAFE_RELEASE_NULL(this->list)就是实现这个目的,其中CC_SAFE_RELEASE_NULL宏作用如下:
2
list->release();
list=nullptr;
|
可见CC_SAFE_RELEASE_NULL宏不仅仅释放对象,还将它的指针设置为nullprt[],也样可以防止野指针。
上述代码还有一个非常重要的关于内存的问题,我们在HelloWorld::init()函数中创建了很多Sprite对象,通过第④行代码this->list->addObject(sprite)将它们放到list容器对象中,它们没有调用addChild函数也没有显式retain函数,然而在当前消息循环结束后它们还是“存活”的,所以在第⑥行Sprite* sprite = (Sprite*)obj中,取出的对象是有效的。这个原因__Array和__Dictionary等容器对象的add相关函数可以使添加对象引用计数加1,相反的remove相关函数可以使添加对象引用计数减1。
Ref内存管理规则
下面我们给出使用Ref对象时候,内存管理一些基本规则:
1、在使用Node节点对象时候,addChild函数可以保持Node节点对象,使引用计数加1。通过removeChild函数移除Node节点对象,使引用计数减1。它们都是隐式调用的,我们不需要关心它们的内存管理。这也正是为什么在前面的章节中我们无数次地使用了Node节点对象,而从来都没有担心过它们内存问题。
2、如果是__Array和__Dictionary等容器对象,可以通过它们add相关函数添加元素会使引用计数加1,相反的remove相关函数删除元素会使引用计数减1。但是前提是__Array和__Dictionary等容器对象本身不没有被释放。
3、如果不属于上面提到的Ref对象,需要保持引用计数,可以显式调用retain函数使引用计数加1,然后显式调用release(或autorelease)函数使引用计数减1。
4、每个 retain函数一定要对应一个 release函数或一个 autorelease函数。
5、release函数使得对象的引用计数马上减1,这是所谓的“斩立决”,但是是否真的释放掉内存要看它的引用计数是否为0。autorelease函数只是在对象上做一个标记,等到消息循环结束的时候再减1,这是所谓的“秋后问斩”,在“秋天”没有到来之前,它的内存一定没有释放,可以安全使用,但是“问斩”之后,是否真的释放掉内存要看它的引用计数是否为0。因此无论是那一种方法,引用计数是为0才是释放对象内存的条件。下面的代码是Ref类的release函数,通过这段代码可以帮助我们理解引用计数。
44
Ref::release()
CCASSERT(_referenceCount>0,
"referencecountshouldgreaterthan0"
);
--_referenceCount;
(_referenceCount==0)
{
#
defined(COCOS2D_DEBUG)&&(COCOS2D_DEBUG>0)
autopoolManager=PoolManager::getInstance();
(!poolManager->getCurrentPool()->isClearing()&&poolManager->isObjectInPools(
))
{
//Triggeranassertifthereferencecountis0buttheRefisstillinautoreleasepool.
//Thishappenswhen'autorelease/release'werenotusedinpairswith'new/retain'.
//
//Wrongusage(1):
//
//autoobj=Node::create();//Ref=1,butit'sanautoreleaseRefwhichmeansitwasintheautoreleasepool.
//obj->autorelease();//Wrong:Ifyouwishtoinvokeautoreleaseseveraltimes,youshouldretain`obj`first.
//
//Wrongusage(2):
//
//autoobj=Node::create();
//obj->release();//Wrong:objisanautoreleaseRef,itwillbereleasedwhenclearingcurrentpool.
//
//Correctusage(1):
//
//autoobj=Node::create();
//|-newNode();//`new`isthepairofthe`autorelease`ofnextline
//|-autorelease();//Thepairof`newNode`.
//
//obj->retain();
//obj->autorelease();//This`autorelease`isthepairof`retain`ofpreviousline.
//
//Correctusage(2):
//
//autoobj=Node::create();
//obj->retain();
//obj->release();//This`release`isthepairof`retain`ofpreviousline.
CCASSERT(
"Thereferenceshouldn'tbe0becauseitisstillinautoreleasepool."
);
}
#endif
delete
;
}
6、一个对象调用autorelease函数,它就会将对象放到自动释放池里,它生命周期自动释放池生命周期息息相关,池在消息循环结束的时候会释放,池会调用池内对象release函数,使得它们的引用计数减1。下面的代码是AutoreleasePool类的清除函数clear(),通过这段代码可以帮助我们理解自动释放池机制。
14
AutoreleasePool::clear()
defined(COCOS2D_DEBUG)&&(COCOS2D_DEBUG>0)
_isClearing=
;
#endif
(
const
auto&obj:_managedObjectArray)
obj->release();
}
_managedObjectArray.clear();
defined(COCOS2D_DEBUG)&&(COCOS2D_DEBUG>0)
;
#endif
}
|
(编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|