【名词解释】

     引用计数:引用计数是现代内存管理中常常使用到的一个概念。它的基本思想是通过计数方式实现多个不同对象同一时候引用一个共享对象,详细地讲,当创建一个对象的实例并在堆上分配内存时,对象的引用计数为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类,一切的起源的更多相关文章

  1. 传智播客C/C++学员荣膺微软&Cocos 2d-x黑客松最佳创新奖

     6月30日,历时32小时的微软开放技术Cocos 2d-x 编程黑客松在北京望京微软大厦成功落下帷幕,这是微软开放技术首次联合Cocos 2d-x 在中国举办黑客松.此次活动共有包括传智播客C/ ...

  2. Cocos 2d-X Lua 游戏添加苹果内购(二) OC和Lua交互代码详解

    这是第二篇 Cocos 2d-X Lua 游戏添加苹果内购(一) 图文详解准备流程 这是前面的第一篇,详细的说明了怎样添加内购项目以及填写银行信息提交以及沙盒测试员的添加使用以及需要我们注意的东西,结 ...

  3. 传智播客C/C++学员荣膺微软&amp;Cocos 2d-x黑客松最佳创新奖

     6月30日,历时32小时的微软开放技术Cocos 2d-x 编程黑客松在北京望京微软大厦成功落下帷幕,这是微软开放技术首次联合Cocos 2d-x 在中国举办黑客松. 此次活动共同拥有包含传智播 ...

  4. 【cocos 2d-x】VS2013+cocos2d-x3.3Final+Adriod交叉编译环境配置(超详细版)

    本系列文章由@二货梦想家张程 所写,转载请注明出处. 作者:ZeeCoder  微博链接:http://weibo.com/zc463717263 我的邮箱:michealfloyd@126.com ...

  5. 用eclipse 玩转cocos 2dx开发

    开始研究cocos2dx,mark一下这个的配置步骤 1 下载eclipse      2 下载android sdk,配置sdk路径,添加环境变量 3 安装adt 4 下载android ndk,配 ...

  6. Cocos 2d-X Lua游戏开发Mac环境搭建以及一点点感悟

    接触Cocos2d-x 最近由于公司项目的需要,自己开始接触Cocos,开始做一些简单的轻量级的游戏,以前没有接触过这一块的东西,也是借助这个机会学习一下游戏的开发,由于以前自己接触的全都是iOS和A ...

  7. cocos 2d-x 3.0配制环境

    cocos2d-x 3.0发布有一段时间了,作为一个初学者,我一直觉得cocos2d-x很坑.每个比较大的版本变动,都会有不一样的项目创建方式,每次的跨度都挺大…… 但是凭心而论,3.0RC版本开始 ...

  8. Cocos 2d-X Lua 游戏添加苹果内购(一) 图文详解准备流程

    事前准备 最近给游戏添加了苹果的内购,这一块的东西也是刚刚做完,总结一下,其实这里不管是游戏还是我们普通的App添加内购这一块的东西都是差不多的,多出来的部分就是我们Lua和OC的交互的部分,以前刚开 ...

  9. 【cocos 2d-x】VS2012+win7+cocos2d-x3.0beta2开发环境配置

    本系列文章由@二货梦想家张程 所写,转载请注明出处. 作者:ZeeCoder  微博链接:http://weibo.com/zc463717263 我的邮箱:michealfloyd@126.com ...

  10. quick Cocos 2dx 学习网站

    http://quick.cocoachina.com/wiki/doku.php?id=zh_cn http://www.cocoachina.com/ http://www.cocoachina. ...

随机推荐

  1. Most common words

    To find the most common words, we can apply the DSU pattern; most_common takes a histogram and retur ...

  2. 6.C语言文件操作之英语电子字典的实现,dos版

    多的不说,直接上代码: 里面涉及的字典文件在这:这是传送门,下载下来以后把该文件放在工程目录下即可 #define _CRT_SECURE_NO_WARNINGS #include <stdio ...

  3. HBase框架基础(三)

    * HBase框架基础(三) 本节我们继续讨论HBase的一些开发常识,以及HBase与其他框架协调使用的方式.在开始之前,为了框架之间更好的适配,以及复习之前HBase的配置操作,请使用cdh版本的 ...

  4. Spring MVC模式示例(未采用解耦控制器)

    Product package com.mstf.bean; import java.io.Serializable; /** * Product类,封装了一些信息,包含三个属性 * @author ...

  5. yii2.0 数据生成 XML 格式。

    return 部分的数组就是你要生成 xml 的数据. 生成的格式如下: yii2.0 中使用 xml 就是这么简单!

  6. HDU-1225 Football Score 模拟问题(水题)

    题目链接:https://cn.vjudge.net/problem/HDU-1225 水题 代码 #include <algorithm> #include <string> ...

  7. caioj 1074 动态规划入门(中链式1:最小交换合并问题)

    经典的石子合并问题!!! 设f[i][j]为从i到j的最大值 然后我们先枚举区间大小,然后枚举起点终点来更新 f[i][j] = min(f[i][k] + f[k+1][j] + sum(i, j) ...

  8. Java 异常的捕获与处理详解(二)

    (一).throws关键字 throws关键字主要是在定义上使用的,表示的是此方法中不进行异常处理,而交给被调用处处理. 例如: class MyMath { public int div(int x ...

  9. 08-for循环

  10. vmware启动虚拟机报错VMware Workstation has paused this virtual machine because the disk on which the virtual machine is stored is almost full. To continue, free an additional 1.4 GB of disk space.

    报错VMware Workstation has paused this virtual machine because the disk on which the virtual machine i ...