cocos2dx 3.1从零学习(四)——内存管理(错误案例分析)
本篇内容文字比較较多,可是这些都是建立在前面三章写代码特别是传值的时候崩溃的基础上的。可能表达的跟正确的机制有出入,还请指正。 假设有不理解的能够联系我。大家能够讨论一下,共同学习。
首先明白一个事实,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从零学习(四)——内存管理(错误案例分析)的更多相关文章
- java虚拟机学习-JVM内存管理:深入垃圾收集器与内存分配策略(4)
Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 概述: 说起垃圾收集(Garbage Collection,下文简称GC),大部分人都把这项 ...
- java虚拟机学习-JVM内存管理:深入Java内存区域与OOM(3)
概述 Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 对于从事C.C++程序开发的开发人员来说,在内存管理领域,他们即是拥有最高权力的皇帝又 ...
- python学习Day9 内存管理
复习 :文件处理 1. 操作文件的三步骤:-- 打开文件:此时该文件在硬盘的空间被操作系统持有 | 文件对象被应用程序持用 -- 操作文件:读写操作 -- 释放文件:释放操作系统对文件在硬盘间的持有 ...
- RT-Thread学习2 —— 内存管理学习记录
RT-Thread学习2 -- 内存管理学习记录1 小内存管理算法(mem.c) 1. 小内存管理法: 小内存管理算法是一个简单的内存分配算法.初始时,它是一块大的内存.当需要分配内存块时,将从这个大 ...
- c语言基础学习08_内存管理
=============================================================================涉及到的知识点有:一.内存管理.作用域.自动变 ...
- Linux-0.11内核源代码分析系列:内存管理get_free_page()函数分析
Linux-0.11内存管理模块是源码中比較难以理解的部分,如今把笔者个人的理解发表 先发Linux-0.11内核内存管理get_free_page()函数分析 有时间再写其它函数或者文件的:) /* ...
- ArcGIS for Desktop入门教程_第四章_入门案例分析 - ArcGIS知乎-新一代ArcGIS问答社区
原文:ArcGIS for Desktop入门教程_第四章_入门案例分析 - ArcGIS知乎-新一代ArcGIS问答社区 1 入门案例分析 在第一章里,我们已经对ArcGIS系列软件的体系结构有了一 ...
- objective-C 的内存管理之-实例分析
objective-C 的内存管理之-实例分析 注:这是<Objective-C基础教程>一书上的实例,但是原书限于篇幅,分析得比较简单,初次阅读看得比较费劲,这里展开详细讨论一下. 场景 ...
- cocos2dx 3.1从零学习(二)——菜单、场景切换、场景传值
回想一下上一篇的内容,我们已经学会了创建一个新的场景scene,加入sprite和label到层中.掌握了定时事件schedule. 我们能够顺利的写出打飞机的主场景框架. 上一篇的内容我练习了七个新 ...
随机推荐
- 实用的表格内省略号和换行(兼容IE6)
让连续的英文数字字符换行显示 word-break: break-all; 让单行文字超出的时候使用点点点表示 white-space: nowrap; overflow: hidden; text- ...
- operator++()和operator++(int)的区别
很久以前(八十年代),没有办法区分++和--操作符的前缀与后缀调用.这个问题遭到程序员的报怨,于是C++语言得到了扩展,允许重载increment 和 decrement操作符的两种形式. 然而有一个 ...
- Android制作曲线、柱状图、饼形等图表——使用AChartEngine
之前在java开发中实现图表使用JFreeChar组件,最近有个小项目要求在Android端进行数据分析,如何实现图表呢?查了一下google提供了一个开源组件Achartengine非常好用,可实现 ...
- linux CentOS7 安装scala
1.打开terminal ,进入当前用户路径: cd /home/sks 2.下载Scala2.11 wget https://downloads.lightbend.com/scala/2.11.8 ...
- Spark RDD关联操作小结
前言 Spark的rdd之间的关系需要通过一些特定的操作来实现, 操作比较多也,特别是一堆JOIN也挺容易让人产生混乱的. 因此做了下小结梳理一下. 准备数据 var rdd1 = sc.makeRD ...
- Cognos Report Studio 链接查询需要注意的地方2
在Report Studio里面用SQL设计报表,查询2,查询3 要链接一般按条件 a1=b1 在选择链接方式需要注意的地方: 默认链接 外部链接 需要设置打开FM,打开报表设计引用的数据包(FM- ...
- [Functional Programming] Fst & Snd, Code interview question
cons(a, b) constructs a pair, and car(pair) and cdr(pair) returns the first and last element of that ...
- [Javascript] Closure Cove, 1
Returning a function from a function, complete with variables from an external scope, is called a cl ...
- android触控,先了解MotionEvent
MotionEvent源代码可以在ocs看到,当然你也可以在SDK中下载源代码,或者其他地方,如: https://github.com/CyanogenMod/android_frameworks_ ...
- htmlayout做的虎鲸宝宝
昨天在auto论坛上偶遇一哥们作的虎鲸宝宝例子,尽管作者提供了auto的源码… … 一个C++老农的悲哀.不过大概看了下流程,最后想想:选什么样的语言已经不重要了. 最重要的是里边的资源文件可以拿来耍 ...