【名词解释】

     引用计数:引用计数是现代内存管理中常常使用到的一个概念。它的基本思想是通过计数方式实现多个不同对象同一时候引用一个共享对象,详细地讲,当创建一个对象的实例并在堆上分配内存时,对象的引用计数为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. zzulioj--1805-- SC和ta的游泳池(简单几何)

    1805: SC和ta的游泳池 Time Limit: 1 Sec  Memory Limit: 128 MB Submit: 154  Solved: 43 SubmitStatusWeb Boar ...

  2. PowerDesigner怎么调出工具箱?

    PowerDesigner怎么调出工具箱? 如图操作:

  3. C#后台请求其它网站页面

    /// <summary> /// 指定Post地址使用Get 方式获取全部字符串 /// </summary> /// <param name="url&qu ...

  4. Android属性动画-Interpolator和ViewPropertyAnimator的用法

    Interpolator的用法 Interpolator这个东西很难进行翻译,直译过来的话是补间器的意思,它的主要作用是可以控制动画的变化速率,比如去实现一种非线性运动的动画效果.那么什么叫做非线性运 ...

  5. CMake入门之创建一个基于PCL的最小工程

    最近在学习PCL,借助Cmake可省去繁琐的添加包含目录和依赖库操作. 一个典型的CMakeLists.txt内容通常为: cmake_minimum_required(VERSION 2.6 FAT ...

  6. 3、Go Exit

    package main import ( "fmt" "os") func main() { //当使用`os.Exit`的时候defer操作不会被运行 所以 ...

  7. Chrome Foundation Services

    Chrome Foundation Services Contents Overview API Standards Service Directory Structure Dependencies ...

  8. nginx设置可以默认访问index.php

    vim /usr/local/nginx/conf/nginx.conf:      在 location /{                index index.php index.html i ...

  9. 题解 CF896C 【Willem, Chtholly and Seniorious】

    貌似珂朵莉树是目前为止(我学过的)唯一一个可以维护区间x次方和查询的高效数据结构. 但是这玩意有个很大的毛病,就是它的高效建立在数据随机的前提下. 在数据随机的时候assign操作比较多,所以它的复杂 ...

  10. hadoop 2.5.1 、Hadoop 2.7 Hadoop 2.6

    1  rpm 安装     yum install rpm 2  JDK安装    << 一定要先删除JDK!!!!!!>>       rpm -qa | grep java ...