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. 我们能够顺利的写出打飞机的主场景框架. 上一篇的内容我练习了七个新 ...
随机推荐
- iframe多层嵌套时获取元素
一.同域:1.父页面获取子页面元素:注意:onload事件jQuery获取:$("iframe").contents().find("holder")..... ...
- hdu 4491 Windmill Animation
A windmill animation works as follows: A two-dimensional set of points, no three of which lie on a l ...
- Framework连接oracle数据库以及Cognos服务器出现错误
1:Framework连接oracle数据库时出现下面错误信息 环境: win2008R2 cognos10.2.1, 服务器上已经安装oracle11.2 content manager连接的也是 ...
- (算法)两个有序数组的第k大的数
题目: 有两个数组A和B,假设A和B已经有序(从大到小),求A和B数组中所有数的第K大. 思路: 1.如果k为2的次幂,且A,B 的大小都大于k,那么 考虑A的前k/2个数和B的前k/2个数, 如果A ...
- (LeetCode74)Search a 2D Matrix
Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the follo ...
- rapidxml 节点加入另一个xml
void TestRapidXml() { ]; sprintf(xmlContent,"<root><head>aaa</head><body&g ...
- 通过WebRTC实现实时视频通信(二)
通过WebRTC实现实时视频通信(一) 通过WebRTC实现实时视频通信(二) 通过WebRTC实现实时视频通信(三) 在上一篇文章中,我们讲解了WebRTC的概述.历史.安全性和开发者工具.接下来我 ...
- 自定义cas客户端核心过滤器AuthenticationFilter
关于cas客户端的基本配置这里就不多说了,不清楚的可以参考上一篇博文:配置简单cas客户端.这里是关于cas客户端实现动态配置认证需要开发说明. 往往业务系统中有些模块或功能是可以不需要登录就可以访问 ...
- Asp.Net 之 前台绑定常用总结
1.<%= 变量名 %> 里面放的是后台定义的变量名,如: <div> <p> <%= DateTime.Now.ToString() %></p ...
- core dump相关
linux下生成core dump文件方法及设置 http://www.2cto.com/os/201310/253450.html 在linux平台下,设置core dump文件生成的方法: 1 ...