[当我在研究Cocos-2dx的源代码时,我在想什么]-Ref类,一切的起源
【名词解释】
引用计数:引用计数是现代内存管理中常常使用到的一个概念。它的基本思想是通过计数方式实现多个不同对象同一时候引用一个共享对象,详细地讲,当创建一个对象的实例并在堆上分配内存时,对象的引用计数为1,在其它对象中须要持有这个共享对象时。须要把共享对象的引用计数加1。当其它对象不再持有该共享对象时,共享对象的引用计数减1,当共享对象的引用计数变成0时。对象的内存会被马上释放。(部分截取自维基百科)。
比較著名的使用引用计数的有COM和Objective-C,在COM的IUnknow接口中定义了三个函数:QueryInterface。AddRef和Release,它们分别用于获取接口对象、给接口对象添加计数,给接口对象降低计数,当内部计数变为0时,自己主动销毁接口对象。
在Objective-C中,则定义了retain,release和autorelease函数,分别用于添加计数、降低计数以及将一个对象交给自己主动释放池对象AutoreleasePool进行管理,由AutoreleasePool对象负责调用release函数。
【Ref类的实现】
因为Cocos2d-x是在Cocos2d-iPhone的基础上发展而来,所以沿用了非常多Objective-C的思想。Ref类的实现就是如此。
Ref类实现了引用计数的功能,它是引擎代码中绝大多数其它类的父类,定义在CCRef.h中。实如今CCRef.cpp中。事实上在CCRef.h文件里不止定义了Ref类。还定义了Clonable类、一系列的宏定义和类型定义。只是我们暂且将精力放在Ref类的解读上。Ref类使用私有成员变量_referenceCount保存计数值。并通过retain,release和autorelease函数实现增减计数值。
class CC_DLL Ref
{
public:
/**
* Retains the ownership.
*
* This increases the Ref's reference count.
*
* @see release, autorelease
* @js NA
*/
void retain(); /**
* Releases the ownership immediately.
*
* This decrements the Ref's reference count.
*
* If the reference count reaches 0 after the descrement, this Ref is
* destructed.
*
* @see retain, autorelease
* @js NA
*/
void release(); /**
* Releases the ownership sometime soon automatically.
*
* This descrements the Ref's reference count at the end of current
* autorelease pool block.
*
* If the reference count reaches 0 after the descrement, this Ref is
* destructed.
*
* @returns The Ref itself.
*
* @see AutoreleasePool, retain, release
* @js NA
* @lua NA
*/
Ref* autorelease(); /**
* Returns the Ref's current reference count.
*
* @returns The Ref's reference count.
* @js NA
*/
unsigned int getReferenceCount() const; protected:
/**
* Constructor
*
* The Ref's reference count is 1 after construction.
* @js NA
*/
Ref(); public:
/**
* @js NA
* @lua NA
*/
virtual ~Ref(); protected:
/// count of references
unsigned int _referenceCount; friend class AutoreleasePool;
};
Ref将构造函数声明为保护类型,防止直接生成Ref对象。在构造函数的成员初始化列表中将引用计数值_referenceCount初始化为1。retain函数将_referenceCount加1,release函数则减1。autorelease函数则将对象托管给AutoreleasePool对象进行管理,详细实现代码例如以下:
NS_CC_BEGIN Ref::Ref() : _referenceCount(1) // when the Ref is created, the reference count of it is 1
{ } Ref::~Ref()
{ } void Ref::retain()
{
CCASSERT(_referenceCount > 0, "reference count should greater than 0");
++_referenceCount;
} void Ref::release()
{
CCASSERT(_referenceCount > 0, "reference count should greater than 0");
--_referenceCount; if (_referenceCount == 0)
{
delete this;
}
} Ref* Ref::autorelease()
{
PoolManager::getInstance()->getCurrentPool()->addObject(this);
return this;
} unsigned int Ref::getReferenceCount() const
{
return _referenceCount;
} NS_CC_END
【Clonable类的定义】
Clonable类定义了复制Ref类对象的规约,类似于Java语言中接口java.lang.Clonable的作用,3.2版本号建议使用clone函数,copy函数已属于废弃函数。
/** Interface that defines how to clone an Ref */
class CC_DLL Clonable
{
public:
/** returns a copy of the Ref */
virtual Clonable* clone() const = 0;
/**
* @js NA
* @lua NA
*/
virtual ~Clonable() {}; /** returns a copy of the Ref.
* @deprecated Use clone() instead
*/
CC_DEPRECATED_ATTRIBUTE Ref* copy() const
{
// use "clone" instead
CC_ASSERT(false);
return nullptr;
}
};
【回调函数的定义】
定义了动作、菜单和调度器的回调函数:
typedef void (Ref::*SEL_CallFunc)();
typedef void (Ref::*SEL_CallFuncN)(Node*);
typedef void (Ref::*SEL_CallFuncND)(Node*, void*);
typedef void (Ref::*SEL_CallFuncO)(Ref*);
typedef void (Ref::*SEL_MenuHandler)(Ref*);
typedef void (Ref::*SEL_SCHEDULE)(float); #define callfunc_selector(_SELECTOR) static_cast<cocos2d::SEL_CallFunc>(&_SELECTOR)
#define callfuncN_selector(_SELECTOR) static_cast<cocos2d::SEL_CallFuncN>(&_SELECTOR)
#define callfuncND_selector(_SELECTOR) static_cast<cocos2d::SEL_CallFuncND>(&_SELECTOR)
#define callfuncO_selector(_SELECTOR) static_cast<cocos2d::SEL_CallFuncO>(&_SELECTOR)
#define menu_selector(_SELECTOR) static_cast<cocos2d::SEL_MenuHandler>(&_SELECTOR)
#define schedule_selector(_SELECTOR) static_cast<cocos2d::SEL_SCHEDULE>(&_SELECTOR)
上面回调函数的定义分为两步:类型定义和宏定义。我们以SEL_CallFuncO为例进行说明,首先通过typedef类型定义了一个成员函数指针SEL_CallFuncO。SEL_CallFuncO是Ref类的成员,同一时候接收Ref类型的指针形參:
typedef void (Ref::*SEL_CallFuncO)(Ref*);
第二步是定义将指定函数转换为SEL_CallFuncO类型函数指针的宏,简化使用者操作:
#define callfuncO_selector(_SELECTOR) static_cast<cocos2d::SEL_CallFuncO>(&_SELECTOR)
上面这个宏CallFuncO_selector的作用就是将_SELECTOR表示的函数,通过static_cast强制转换为SEL_CallFuncO类型的函数指针的定义。
【内存泄漏的检測】
在上面解说Ref实现过程中,我们有益忽略了一些次要的代码,当中就包含内存泄漏检測。这部分代码是以宏:
#define CC_USE_MEM_LEAK_DETECTION 0
作为开关的。
内存泄漏检測代码主要包含Ref类静态成员函数:
class CC_DLL Ref
{
// Memory leak diagnostic data (only included when CC_USE_MEM_LEAK_DETECTION is defined and its value isn't zero)
#if CC_USE_MEM_LEAK_DETECTION
public:
static void printLeaks();
#endif
};
定义在CCRef.cpp文件内的静态函数(静态函数与普通函数不同之处在于。它仅仅在声明它的文件里可见,其它文件不可见。同一时候,其它文件里能够定义同样名字的函数,不会发生冲突)
#if CC_USE_MEM_LEAK_DETECTION
static void trackRef(Ref* ref);
static void untrackRef(Ref* ref);
#endif
trackRef函数在Ref类对象创建的时候调用,untrackRef在Ref类对象销毁的时候调用。Ref对象实例保存在静态链表__refAllocationList中,实现代码例如以下所看到的:
#if CC_USE_MEM_LEAK_DETECTION static std::list<Ref*> __refAllocationList; void Ref::printLeaks()
{
// Dump Ref object memory leaks
if (__refAllocationList.empty())
{
log("[memory] All Ref objects successfully cleaned up (no leaks detected).\n");
}
else
{
log("[memory] WARNING: %d Ref objects still active in memory.\n", (int)__refAllocationList.size()); for (const auto& ref : __refAllocationList)
{
CC_ASSERT(ref);
const char* type = typeid(*ref).name();
log("[memory] LEAK: Ref object '%s' still active with reference count %d.\n", (type ? type : ""), ref->getReferenceCount());
}
}
} static void trackRef(Ref* ref)
{
CCASSERT(ref, "Invalid parameter, ref should not be null!"); // Create memory allocation record.
__refAllocationList.push_back(ref);
} static void untrackRef(Ref* ref)
{
auto iter = std::find(__refAllocationList.begin(), __refAllocationList.end(), ref);
if (iter == __refAllocationList.end())
{
log("[memory] CORRUPTION: Attempting to free (%s) with invalid ref tracking record.\n", typeid(*ref).name());
return;
} __refAllocationList.erase(iter);
} #endif // #if CC_USE_MEM_LEAK_DETECTION
[当我在研究Cocos-2dx的源代码时,我在想什么]-Ref类,一切的起源的更多相关文章
- 传智播客C/C++学员荣膺微软&Cocos 2d-x黑客松最佳创新奖
6月30日,历时32小时的微软开放技术Cocos 2d-x 编程黑客松在北京望京微软大厦成功落下帷幕,这是微软开放技术首次联合Cocos 2d-x 在中国举办黑客松.此次活动共有包括传智播客C/ ...
- Cocos 2d-X Lua 游戏添加苹果内购(二) OC和Lua交互代码详解
这是第二篇 Cocos 2d-X Lua 游戏添加苹果内购(一) 图文详解准备流程 这是前面的第一篇,详细的说明了怎样添加内购项目以及填写银行信息提交以及沙盒测试员的添加使用以及需要我们注意的东西,结 ...
- 传智播客C/C++学员荣膺微软&Cocos 2d-x黑客松最佳创新奖
6月30日,历时32小时的微软开放技术Cocos 2d-x 编程黑客松在北京望京微软大厦成功落下帷幕,这是微软开放技术首次联合Cocos 2d-x 在中国举办黑客松. 此次活动共同拥有包含传智播 ...
- 【cocos 2d-x】VS2013+cocos2d-x3.3Final+Adriod交叉编译环境配置(超详细版)
本系列文章由@二货梦想家张程 所写,转载请注明出处. 作者:ZeeCoder 微博链接:http://weibo.com/zc463717263 我的邮箱:michealfloyd@126.com ...
- 用eclipse 玩转cocos 2dx开发
开始研究cocos2dx,mark一下这个的配置步骤 1 下载eclipse 2 下载android sdk,配置sdk路径,添加环境变量 3 安装adt 4 下载android ndk,配 ...
- Cocos 2d-X Lua游戏开发Mac环境搭建以及一点点感悟
接触Cocos2d-x 最近由于公司项目的需要,自己开始接触Cocos,开始做一些简单的轻量级的游戏,以前没有接触过这一块的东西,也是借助这个机会学习一下游戏的开发,由于以前自己接触的全都是iOS和A ...
- cocos 2d-x 3.0配制环境
cocos2d-x 3.0发布有一段时间了,作为一个初学者,我一直觉得cocos2d-x很坑.每个比较大的版本变动,都会有不一样的项目创建方式,每次的跨度都挺大…… 但是凭心而论,3.0RC版本开始 ...
- Cocos 2d-X Lua 游戏添加苹果内购(一) 图文详解准备流程
事前准备 最近给游戏添加了苹果的内购,这一块的东西也是刚刚做完,总结一下,其实这里不管是游戏还是我们普通的App添加内购这一块的东西都是差不多的,多出来的部分就是我们Lua和OC的交互的部分,以前刚开 ...
- 【cocos 2d-x】VS2012+win7+cocos2d-x3.0beta2开发环境配置
本系列文章由@二货梦想家张程 所写,转载请注明出处. 作者:ZeeCoder 微博链接:http://weibo.com/zc463717263 我的邮箱:michealfloyd@126.com ...
- quick Cocos 2dx 学习网站
http://quick.cocoachina.com/wiki/doku.php?id=zh_cn http://www.cocoachina.com/ http://www.cocoachina. ...
随机推荐
- rest_framework 分页三种
.分页 a. 分页 看第n页 每页显示n条数据: b. 分页 在某个位置 向后查看多少条数据 c. 加密分页 上一页和下一页 本质:查看 记住页码id的最大值和最小值 通过其来准确扫描 过去的话 会从 ...
- Nginx下部署Laravel项目
Nginx下部署Laravel项目 标签(空格分隔): php Nginx配置文件 listen 80 default_server; #listen [::]:80 default_server i ...
- Maven项目:Plugin execution not covered by lifecycle configuration 解决方案
这个是eclipse中配置文件pom.xml报的错.具体错误信息: Plugin execution not covered by lifecycle configuration: org.apach ...
- Servlet设置Cookie无效
项目中保存用户信息用到了Cookie,之前没有太注意,今天怎么设置Cookie都无效,断点跟了无数遍,都没有找出问题所在,明明发送Cookie的代码都有执行,可是愣是找不到Cookie发送到哪里去了, ...
- JDOJ 2939: Suffix Automaton 广义后缀自动机_统计子串
建立广义后缀自动机,对每个节点都建立各自的 $Parent$ 数组. 这样方便统计,不会出现统计错误. 考虑新加入一个字符. 1 这条转移边已经存在,显然对答案没有贡献. 2 这条转移边不存在,贡献即 ...
- NodeJS学习笔记 进阶 (2)Nodejs进阶:MD5加密算法(ok)
个人总结:这篇文章讲解了Nodejs中自带模块的MD5加密算法的使用,读完这篇文章需要15分钟,其实还有一个叫utility的包在npm上,也非常好用. 摘选自网络 简介 MD5(Message-Di ...
- Android studio树形
原创作品,允许转载,转载时请务必声明作者信息和本声明. http://www.cnblogs.com/zhu520/p/8349553.html 这个是上网找了好久才弄出来的,我把我上网找的总结也写 ...
- 洛谷 P1443 马的遍历
P1443 马的遍历 题目描述 有一个n*m的棋盘(1<n,m<=400),在某个点上有一个马,要求你计算出马到达棋盘上任意一个点最少要走几步 输入输出格式 输入格式: 一行四个数据,棋盘 ...
- hdoj--5333--Dancing Stars on Me(水题)
Dancing Stars on Me Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Ot ...
- 生成不重复的随机数对(C/C++)
1 #include <stdio.h> #include <algorithm> #include <stdlib.h> #include <time.h& ...