[当我在研究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. ...
随机推荐
- BZOJ5042: LWD的分科岛
[传送门:BZOJ5042] 简要题意: 给出n个数,q个询问,每个询问输入opt,l,r,如果opt=1,则输出l到r中的最小值,否则输出最大值 题解: 直接上ST表,自信一波,结果 MLE??好吧 ...
- iOS 报错:(子线程中更新UI)This application is modifying the autolayout engine from a background thread after the engine was accessed from the main thread. This can lead to engine corruption and weird crashes.
今天在写程序的时候,使用Xcode 运行工程时报出下面的错误错信息,我还以为是什么呢,好久没遇到过这样的错误了. **ProjectName[1512:778965] This application ...
- SparkStreaming基础
* SparkStreaming基础 打开之前构建好的Maven工程,如何构建?请参看SparkCore基础(二)的最后部分. 在SparkCore中,我们操作的数据都在RDD中,是Spark的一个抽 ...
- AppManager类,管理Activity和App
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); / ...
- 关于Mantle使用个人的一些见解
前一个月,我接触到了Mantle,由于项目采用的是MVC的设计模式,选用好的model也是至关重要的.先介绍下Mantle的使用吧. 首先定义好数据模型: @property (nonatomic, ...
- PostgreSQL+pgpooll+Keepalived双机HA方案
PostgreSQL+pgpooll+Keepalived双机HA方案 (注:本文档中的所有操作在没有说明的情况下都应该使用postgres用户进行操作,postgres用户没有权限的操作方可采用ro ...
- C++中inline函数
(一)inline函数(摘自C++ Primer的第三版) 在函数声明或定义中函数返回类型前加上关键字inline即把min()指定为内联. inline int min(int first, int ...
- 将毫秒时间转换为yyyy-MM-dd HH:mm:ss格式
/** * 将毫秒时间转换为yyyy-MM-dd HH:mm:ss格式 */ public static String getDateFromTimeMillis(Long timeMillis) { ...
- 去除input的前后的空格
这里用的是jquery的方法
- springboot整合redis,并解决乱码问题。
热烈推荐:超多IT资源,尽在798资源网 springboot 版本为 1.5.9 //如果是2.x 修改 pom.xml 也可切换成 1.5.9 <parent> <groupId ...