本篇内容文字比較较多,可是这些都是建立在前面三章写代码特别是传值的时候崩溃的基础上的。可能表达的跟正确的机制有出入,还请指正。 假设有不理解的能够联系我。大家能够讨论一下,共同学习。

首先明白一个事实,retain和release是一一相应的,跟new和delete一样。

1.引用计数retain release

这里请參考一下引用计数的书籍,肯定说的比我讲的具体。

简单一点理解就是,对new的指针加一个计数器,每引用一次这块内存。计数就加1。

在析构的时候减1。假设等于0的时候就delete这个指针并置空。

2.自己主动释放autolease

autorelease后的对象默认计数是1。而且autorelease的对象会被放到自己主动释放池里。自己主动释放池这里有一个须要注意的地方,自己主动释放池存储了当前帧全部的autorelease的对象,在帧结束时对当中全部对象release一次。处理完后这个释放池就不再拥有对这些对象的处理权,也就是说自己主动释放池仅仅会最当中的对象进行一次release操作。

释放的同一时候使用一个新的释放池存储后一帧定义的autorelease对象。如此循环下去。

精灵们create函数运行后会被放到自己主动释放池,释放池会在每帧结束的时候调用,对于引用计数为1的内存进行释放。假设没有其它操作比方retain或者addchild的话,那么引用计数没有添加。当前帧结束后计数减1为0后。这个指针也就不复存在了。

什么时候计数会加1?

手动调用retain使引用技术加1;

cocos2dx我所见过的create静态方法都是调用autorelease的,计数默觉得1。

每引用一次,比方使用频率最多的addChild()会使其引用技术加1。

什么时候计数会减1?

手动调用release使引用技术减1;

自己主动释放池里的会在当前帧结束的时候减1。注意是当前帧,后面的释放池里存储的是后面帧执行时定义的autorelease对象。

假设一个场景析构,会对全部的子节点release一次。这被称为链式反应。

链式反应解释例如以下:我们当前执行这一个场景。场景初始化,加入了非常多层,层里面有其他的层或者精灵。而这些都是 CCNode节点,以场景为根,形成一个树形结构。场景初始化之后(一帧之后),这些节点将全然 依附 (内部通过 retain) 在这个树形结构之上。全权交由树来管理。当我们 砍去一个树枝。或者将树 连根拔起。那么在它之上的“子节点”也会跟着去除(内部通过release)。这便是链式反应。来自 <http://www.tairan.com/archives/4184>

错误案例:

我们在create后,假设不使用retain使引用计数加1的话,那么自己主动释放池会使其引用计数减1,假设在回调函数中使用addchild(sp)会崩溃。

要想解决问题,在create后加入使用sp->retain();来添加它的引用计数。

例如以下:

     auto temp = Sprite::create("CloseNormal.png");temp->retain();//假设凝视掉会崩溃。auto item4 =MenuItemLabel::create(Label::createWithBMFont("fonts/futura-48.fnt","Hell"), \[=](Ref * ref){addChild(temp);});

有些人可能会使用引用的lambda表达式。例如以下:

    auto  temp =Sprite::create("CloseNormal.png");temp->retain();auto item4 =MenuItemLabel::create(Label::createWithBMFont("fonts/futura-48.fnt","Hell"), \[&](Ref * ref){addChild(temp);});

崩溃了!

引用的话 即使retain也会崩溃。这个为什么呢?

引用的话我们使用的是temp的别名引用,也就指向指针的指针temp。

当这个函数运行完的时候temp做为局部变量就会被释放。所以我们在回调函数中使用的temp已经不存在了。 假设是=赋值的话,精灵的指针会拷贝一份传到lambda表达式中。所以不会崩溃。

要想解决引用崩溃的问题,我们仅仅要使temp不会被释放就好。所以定义为成员变量能够解决引用的lambda表达式造成的问题。大家能够尝试一下。

深入理解CC_SYNTHESIZE_RETAIN

假装我们从未学习过CC_SYNTHESIZE_RETAIN。第二篇讲过场景之间的正向传值,假设我们在主场景create一个精灵,然后赋值给下一个场景的成员变量Sprite *sp。对于这样的autorelease的变量我们应该怎么进行传值操作呢?

autorelease变量会在每一帧结束的时候计数减1进行销毁。所以我们应该对其计数加1,避免下个场景使用的时候已经被删除。

我们应该在主场景切换场景的时候这样写:

voidMainScene::Morning_0623_MemoryManage(cocos2d::Ref * ref)
{auto scene = MemoryManage::createScene();auto memLayer = (MemoryManage *)scene->getChildren().at(0);tmpSp =Sprite::create("coc/buildings_lowres/59.0.png");//注意斜杠的方向tmpSp->retain();//引用计数加1,否则当前帧结束会被销毁memLayer->sp = tmpSp;//假设不retain的话会被自己主动释放掉   在切换场景的时候会被释放掉。Director::getInstance()->pushScene(scene);
}

在下个场景MemoryManage定义成员变量sp的时候应该对其进行初始化,由于它是一个指针。

我们应该定义Sprite *sp = nullptr;

否则在MainScene复制的时候会崩溃,由于它的一个未知的指针,指向了内存中未知的区域。

崩溃的地方例如以下:

断言失败    CCASSERT(_referenceCount > 0,"reference count should greater than 0");

由于这个时候sp是一个未知的指针。

以下我们对主场景中

tmpSp =Sprite::create("coc/buildings_lowres/59.0.png");创建的精灵的整个生命周期的引用计数进行分析。

主场景create时autorelease(1)->retain(2)->autorelease自己主动释放池release(1)->在子场景中被addchild(2)->子场景析构的链式反应(1)->???

请看子场景析构的时候计数还是1,这会造成内存泄露。所以我们应该在析构函数中运行一次sp->release().手动减1。

CC_SYNTHESIZE_RETAIN的出现就是为了解决上述问题,它仅仅是把retain和release操作包装了一下。

这个时候你再去看一遍CC_SYNTHESIZE_RETAIN的源代码:

#defineCC_SYNTHESIZE_RETAIN(varType, varName, funName)    \
private: varTypevarName; \
public: virtualvarType get##funName(void) const { return varName; } \
public: virtual voidset##funName(varType var)   \
{ \if (varName != var) \{ \CC_SAFE_RETAIN(var); \CC_SAFE_RELEASE(varName); \varName = var; \} \
}

调用CC_SYNTHESIZE_RETAIN来给成员变量赋值时。会对原来的变量进行一次retain操作。然后须要我们在析构函数的时候加入相应的 CC_SAFE_RELEASE(varName);

如今说一下为什么在CC_SYNTHESIZE_RETAIN中对成员变量varName运行CC_SAFE_RELEASE(varName);

varName假设被不同的变量多次赋值会怎么样? 每一次的赋值原来的变量都要做一次retain操作,假设我们直接改变了varName的值而不改变它原来指向的内存的引用计数的话,那么就会造成内存泄露。

所以每次赋值都会对原来的内存进行一次release。

总结:retain和release是一一相应的,可是我们应该使用它们的加强版。宏定义CC_SAFE_RETAIN和CC_SAFE_RELEASE。这两个可不是一一相应的。

比方我们 CC_SYNTHESIZE_RETAIN定义的变量,仅仅在析构函数中加一句CC_SAFE_RELEASE。

cocos2dx 3.1从零学习(四)——内存管理(错误案例分析)相关推荐

  1. 内存管理实战案例分析3:为何分配不出一个页面?

    微信公众号:奔跑吧linux社区 本文节选自<奔跑吧Linux内核>第二版卷1第6.3.3章 1.问题描述 下面是有问题的OOM Killer内核日志,其中空闲页面为86048KB,最低警 ...

  2. ArcGIS for Desktop入门教程_第四章_入门案例分析 - ArcGIS知乎-新一代ArcGIS问答社区...

    原文:ArcGIS for Desktop入门教程_第四章_入门案例分析 - ArcGIS知乎-新一代ArcGIS问答社区 1 入门案例分析 在第一章里,我们已经对ArcGIS系列软件的体系结构有了一 ...

  3. MTK:内存管理机制简单分析

    MTK内存管理机制简单分析 1:内存: 内存,在手机里面,是个较为紧缺的资源,特别是在功能机上面.经常在功能机上面产生的内存不足,申请失败的地方比比皆是, 更是屡见不鲜,经常会为了节省内存,会进行代码 ...

  4. java 内存分配实例_java学习(四) —— 内存分配浅析

    前言 java中有很多类型的变量.静态变量.全局变量及对象等,这些变量在java运行的时候到底是如何分配内存的呢?接下来有必要对此进行一些探究. 基本知识概念: (1)寄存器:最快的存储区, 由编译器 ...

  5. cocos2dx 3.1从零学习(一)——入门篇(一天学会打飞机)

    没办法,浏览这么高,为啥没人投票呢?朋友们,我这篇文章參加了csdn博文大赛.喜欢的来点个赞吧!点击:http://vote.blog.csdn.net/Article/Details?article ...

  6. Objective-C学习之旅(四)----内存管理2----retain点语法

    一.retain属性的主要作用 1.O-C内存管理和点语法 1>OC内存管理正常情况要使用大量的retain和relrese操作 2>点语法可以减少使用retain和release的操作 ...

  7. Cocos2d-x学习笔记—内存管理机制

    Cocos2d-x 3.x内存管理机制 1:C++内存管理 1-1:内存分配区域 创建对象需要两个步骤:第一步,为对象分配内存:第二步,调用构造函数初始化内存.在第一步中,可以选择几个不同的分配区域. ...

  8. C语言学习笔记 —— 内存管理

    一.内存模型 对于一个C语言程序而言,内存空间主要由五个部分组成 代码段(text).数据段(data).未初始化数据段(bss),堆(heap) 和 栈(stack) 组成,其中代码段,数据段和BS ...

  9. 深入学习python内存管理

    深入Python的内存管理 作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 语言的内存管理是语言设计的一个重要方面.它是决定语言性 ...

  10. python内存管理可以使用del_Python深入学习之内存管理

    语言的内存管理是语言设计的一个重要方面.它是决定语言性能的重要因素.无论是C语言的手工管理,还是Java的垃圾回收,都成为语言最重要的特征.这里以Python语言为例子,说明一门动态类型的.面向对象的 ...

最新文章

  1. str_pad函数php,str_pad
  2. webView 显示一段 html 代码
  3. 个人电脑 公司电脑 代理_这样的电脑谁来用?一体式水冷,磁悬浮风扇!
  4. android loading封装_我们经常用的Loading动画居然还有这种姿势
  5. 【转】ubuntu16.04安装配置tftp服务
  6. vba代码编程800例_VBA编程常用过程代码实例2630
  7. Linux服务器编程 用SIGURG检测带外数据是否到达
  8. 2.4. myisamchk — MyISAM Table-Maintenance Utility
  9. 1.1万亿!华为第一!
  10. linux 内存泄露 工具,Linux Kernel模块内存泄露分析
  11. C++ unsigned long 转化为 unsigned char*
  12. lua mysql 存储类型_Lua学习----Lua基础数据类型
  13. java监控文件内容变化_Java使用WatchService监控文件内容变化的示例
  14. 素数猜想对 c语言,1007 素数对猜想 (20 分)(C语言实现)
  15. WPF基础五:UI①布局元素StackPanel
  16. AI加持,云之家V10让移动办公充满无限想象
  17. win10专业版安装.net Framework4.7.2出现问题解决办法
  18. 水深注记采用渐变颜色的编程实现
  19. java中写html标签,java写html剔出标签
  20. 什么是Rest风格的Api?

热门文章

  1. github 远程仓库操作
  2. C# 中值类型[Required(*****)] 报错
  3. 复习HTTP状态码+301和302
  4. zend studio【快捷键】
  5. 线上只执行一次的脚本编写注意事项
  6. Handsontable 自定义菜单 自定义命令存放位置
  7. hiredis中异步的实现小结
  8. HDU 4597 Play Game (记忆化搜索博弈DP)
  9. jQuery图片轮播插件 jQuery Cycle Plugin
  10. [技巧心得] 嵌套选择器优先级