///////////////////////////////////
//author : zhxfl
//date   : 2013.8.29
//email  : 291221622@qq.com
///////////////////////////////////
 
在看这个之前,你先要了解onEnter , onExit 和 构造函数,析构函数在调用顺序上面的区别
总的来说,顺序如下
 
构造函数{}
onEnter{}
onExit{}
析构函数{}
 
讲这个之前需要讲一些在实践中遇到的问题。前段时间在项目中fix掉两个和内存相关的bug,然后就对这两个方法存在有一些新的理解。
 
1 我们用的cocos2d-x的版本是cocos2d-1.0.1-x-0.12.0,那时候还没有CCListView.cpp这个列表控件,所以项目组就自己写了这个控件。我们知道列表控件的需要放图片或者文字等等信息,所以必须写成虚类。如下
 
struct IListItem:
{
//how we draw this item
virtual cocos2d::CCNode *createNode(float width) = ;
virtual ~IListItem() {}
};
 
对应的Item需要继承IListView才能来编写这个接口,如下例子

class FightItem : public IListItem
{
public: FightItem(const DailyProperty &dp);
~FightItem();
virtual cocos2d::CCNode *createNode(float width);
virtual void onExit();
void selected();
void unselected(); static FightItem *itemWithFight(const DailyProperty &dp);
const DailyProperty &getDailyProperty()
{
return m_dp;
} private:
cocos2d::CCNode *m_node;
};
 
看完这个接口,如果我们申明了FightItem *item(下面都是用item来指FightItem实例),那么item的释放我们只能使用delete了。本来用delete也没什么关系,随着需求的变化,我们需要用到一些高级的方法,例如在FigthItem里面。这时候我们就需要调用CCCallFunc::actionWithTarget(this,callfunc_selector(FigthItem::change) 类似于这样的方法。这里就要求FightItem继承CCObject才能使用callfunc_selector。所以FightItem的声明被改成了
 
class FightItem : public IListItem, cocos2d::CCObject
{
public: FightItem(const DailyProperty &dp);
~FightItem();
virtual cocos2d::CCNode *createNode(float width);
virtual void onExit();
void selected();
void unselected(); static FightItem *itemWithFight(const DailyProperty &dp);
const DailyProperty &getDailyProperty()
{
return m_dp;
} private:
cocos2d::CCNode *m_node;
void change();
};
 
到这里,内存管理就开始出现问题了,就是release和delete的混用的问题,FightItem现在是由引用计数管理着,而CCListView里面就用delete把FightItem释放了,然后引用计数仍然认为FightItem还没释放,这里就是double free,释放两次的错误了。
 
到这里,需要总结一句,框架引入了引用计数做内存管理的时候,尽量不用混用delete,否则其他人不知道混用了,就会出这种问题。
很自然的,CCListView里面也采用 FightItem * item; item->release(),这样的释放机制,问题就决绝了。接着,下一波问题出现了。
再次出现的是泄露问题了。
 
我们需要在FightItem 里面播放一个动画,这时我们有个地方就用了如下的方法
 
CCScheduler::sharedScheduler()->scheduleSelector(
schedule_selector(FightItem::showTimeDescription), this, 0.5f, false);
 
好的,现在我们的析构函数这样写
 
FightItem::~FightItem()
{
CCScheduler::sharedScheduler()->unscheduleSelector(
schedule_selector(FightItem::showTimeDescription), this);
if(m_node) m_node->release();
}
 
这样看不出来有什么问题是吧,不过在偶然的情况下,我发现这个析构函数根本不执行。真的很偶然,这样的泄露确实是很难找出来的,所以先建立起这个意识确实很重要,等到项目代码多了出这种问题,就要整个项目排查了。下面分析一下为什么不执行。
 
FightItem创建的时候count[引用计数]为1, 执行
CCScheduler::sharedScheduler()->scheduleSelector(
          schedule_selector(FightItem::showTimeDescription), this, 0.5f, false);
的时候,引用计数为2,加入CCListView引用计数为3(注意当引用计数为1的时候,会被系统直接释放掉)。说明CCScheduler占用的了FightItem。按照原来的删除过程,CCListView执行item->release(),FightItem引用计数变为2,还被CCScheduler占用。按照C++的机制,delete没有执行,~FightItem也没有进入所以
CCScheduler::sharedScheduler()->unscheduleSelector(
schedule_selector(FightItem::showTimeDescription), this);
函数虽然写在里面了,可是没有执行的机会,这样item永远没有释放的机会。听起来像不像数据库里面的死锁啊。
 
终于可以开始写主题了,这时候我们就需要虚函数onExit了,分写修改如下:
struct IListItem:public cocos2d::CCObject
{
//how we draw this item
virtual cocos2d::CCNode *createNode(float width) = ;
virtual ~IListItem() {}
virtual void onExit() {}
}; FightItem::~FightItem()
{
if(m_node) m_node->release();
} void FightItem::onExit()
{
CCScheduler::sharedScheduler()->unscheduleSelector(
schedule_selector(FightItem::showTimeDescription), this);
}
这时候CCListView执行item->release()改成这样
item->onExit()
item->release();
onExit()把CCScheduler的引用清掉,接着CCListView执行release(),这时候引用计数为0,系统就会执行delete了,这时候~FightItem()就会进入了。
 
这时候就可以得出最后结论了,onEnter,onExit是配合引用计数机制存在的,总的来说,所有造成本对象被其他对象引用的操作,都放在onEnter里面,所有去掉本对象被其他对象引用的操作都放在onExit里面,这样就能保证本对象在释放的时候能够成功了。
 
所以,我们一般都是写成如下的规范,就很少出现内存泄露问题了
void CCImageBtn::onEnter()
{
CCNode::onEnter();
CCTouchDispatcher::sharedDispatcher()->addTargetedDelegate(this, m_touchPriority, true);
} void CCImageBtn::onExit()
{
CCTouchDispatcher::sharedDispatcher()->removeDelegate(this);
CCNode::onExit();
}

cocos2d-x 从onEnter、onExit、 引用计数 谈内存泄露问题的更多相关文章

  1. 【微信小程序项目实践总结】30分钟从陌生到熟悉 web app 、native app、hybrid app比较 30分钟ES6从陌生到熟悉 【原创】浅谈内存泄露 HTML5 五子棋 - JS/Canvas 游戏 meta 详解,html5 meta 标签日常设置 C#中回滚TransactionScope的使用方法和原理

    [微信小程序项目实践总结]30分钟从陌生到熟悉 前言 我们之前对小程序做了基本学习: 1. 微信小程序开发07-列表页面怎么做 2. 微信小程序开发06-一个业务页面的完成 3. 微信小程序开发05- ...

  2. Qt浅谈内存泄露(总结)

    Qt浅谈内存泄露(总结) 来源 http://blog.csdn.net/taiyang1987912/article/details/29271549 一.简介 Qt内存管理机制:Qt 在内部能够维 ...

  3. 关于lua闭包导致引用无法释放内存泄露

    最近项目存在严重的内存泄漏问题,每次切level 会增加20M无法释放的内存,翻遍了项目用了多个工具,查询资料等 发现项目中两种存在内存泄露的情况 1.lua闭包的不当使用,对比包的引用要及时 释放. ...

  4. 深入理解 PHP7 中全新的 zval 容器和引用计数机制

    深入理解 PHP7 中全新的 zval 容器和引用计数机制 最近在查阅 PHP7 垃圾回收的资料的时候,网上的一些代码示例在本地环境下运行时出现了不同的结果,使我一度非常迷惑. 仔细一想不难发现问题所 ...

  5. COM编程之四 引用计数

    [1]客户为什么不应直接控制组件的生命期? 假设一个组件A正在使用另一个组件B,可想组件A(客户)代码中肯定有若干个指向组件B接口的指针. 那么这种情况下,当使用完一个接口而仍然在使用另一个接口时,是 ...

  6. ARC————自动引用计数

    一.内存管理/引用计数 1.引用计数式内存管理的方式(下面四种) 对象操作 OC方法 生成并持有对象 alloc/new/copy/mutableCopyd等方法 持有对象 retain方法 释放对象 ...

  7. iOS中引用计数内存管理机制分析

    在 iOS 中引用计数是内存的管理方式,虽然在 iOS5 版本中,已经支持了自动引用计数管理模式,但理解它的运行方式有助于我们了解程序的运行原理,有助于 debug 程序. 操作系统的内存管理分成堆和 ...

  8. cocos2dx中的内存管理机制及引用计数

    1.内存管理的两大策略: 谁申请,谁释放原则(类似于,谁污染了内存,最后由谁来清理内存)--------->适用于过程性函数 引用计数原则(创建时,引用数为1,每引用一次,计数加1,调用结束时, ...

  9. php 垃圾回收机制----写时复制和引用计数

    PHP使用引用计数和写时复制来管理内存.写时复制保证了变量间复制值不浪费内存,引用计数保证了当变量不再需要时,将内存释放给操作系统. 要理解PHP内存管理,首先要理解一个概念----符号表. 符号表的 ...

随机推荐

  1. emctl start dbconsole OC4J_dbconsole*** not found

    C:\windows\system32>emctl start dbconsole OC4J Configuration issue. D:\app\product\\db_1/oc4j/j2e ...

  2. WPF获取控件的句柄

    在WinForm中,获得句柄是一件很容易的事情,This.Handle或者Control.Handle就可以,最近在WPF的开发中发现找不到这个属性,一番查找资料后找到了两种方式. 1,使用Windo ...

  3. git 提交

    git rebase -i 在使用git开发的时候经常会面临一个常见的问题.多个commit 需要合并为一个完整的commit提交. 合并多个commit为一个完整的commit 我先基于develo ...

  4. 敏捷开发的价值观(转自MBAlib)

    敏捷开发的价值观 实际上敏捷开发运动在数年前就开始了,但它正式开始的标志是2001年2月的“敏捷宣言”(Agile Manifesto),这项宣言是由17位当时称之为“轻量级方法学家”所编写签署的,他 ...

  5. Mysql不区分大小写

    mysql在windows下表名是不区分大小写的,但是在linux下是区分大小写的: 今天帮朋友解决安装到ubuntu系统下mysql不区分大小写的问题,最初在/etc/my.cnf文件中的[mysq ...

  6. java web第一个Servlet程序

    Servlet 简介 . Java Servlet是和平台无关的服务器端组件,它运行在Serlet容器中.Servlet容器负责Servlet和客户的通信以及调用Servlet的方法,Servlet和 ...

  7. 1007: [HNOI2008]水平可见直线

    先对a排序,a相等的话就对b排序: 维护一个栈,每次取栈的头两个,和当前的直线相比较: 如果当前的直线把头第一个屏蔽,就将他出栈,一直到不能屏蔽为止: 代码: #include<cstdio&g ...

  8. ORMBase对象/关系型数据库映射在MVC中的应用(二)

    3.DataBase基类,查询方法返回值是List<T>,并且是分页的,ThePart.dll版本2.0中封装了一个PageInfo类,作为分页的类型.这种方法很机械,也很狗血..建议大家 ...

  9. Linux用户行为日志审计

    http://my.oschina.net/xiangpang/blog/532999 http://my.oschina.net/chaichuan/blog/508494 http://my.os ...

  10. 增加Android可用内存

    In the development of TV applications, especially when dealing with images were more likely to feel ...