CCObject是绝大部分cocos2d-x类的基类, 我们就从这里一步一步揭开cocos2d-x的奥秘.
CCObject承担了两个重要的功能, 拷贝机制和内存管理.
##拷贝机制
打开CCObject.h
首先看到的是类CCCopying, 而CCObject是从该类派生. 从这个意义上来讲CCCopying才是大部分类的基类.
但CCCopying其实非常简单, 再加上没有其他类从CCCopying派生, 所以说CCObject才是大部分类的基类也是不错的. 我认为CCCopying仅仅是一个接口性质.
CCCopying这个类非常简单, 只有一个成员函数copyWithZone(CCZone *pZone); 简单到其实现只是为了报错. 尽管如此, 但其却承担了拷贝机制这一重要的功能.
cocos2d-x和cocos2d-iphone是近亲, 所以cocos2d-x在API上会和cocos2d-iphone保持一致, 代码上也多有借鉴. CCObject明显就有很多NSObject的痕迹.
当然我对Objective-c并不熟悉, 这里都是些猜测罢了. copyWithZone或许就是借鉴了Obj-c.
我们先来看下拷贝机制, 至于CCObject的代码倒不忙着看.
这两段代码来至于类CCArray, 我们可以看到调用了原对象的copy()来拷贝一份新的CCArray.
而copy()则定义于CCObject中, 其工作就是调用copyWithZone.
CCArray1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| CCArray* CCArray::createWithArray(CCArray* otherArray) { CCArray* pRet = (CCArray*)otherArray->copy(); pRet->autorelease(); return pRet; } CCObject* CCArray::copyWithZone(CCZone* pZone) { CCAssert(pZone == NULL, "CCArray should not be inherited."); CCArray* pArray = new CCArray(); pArray->initWithCapacity(this->data->num > 0 ? this->data->num : 1); CCObject* pObj = NULL; CCObject* pTmpObj = NULL; CCARRAY_FOREACH(this, pObj) { pTmpObj = pObj->copy(); pArray->addObject(pTmpObj); pTmpObj->release(); } return pArray; }
|
这里面牵涉到两个概念, 深拷贝和浅拷贝. 不清楚的大家可以搜索下, 简而言之, 深拷贝才真正的完全拷贝. cocos2d-x实现的是深拷贝.
我们在上面CCArray::copyWithZone中可以看到拷贝时对array的各个成员也执行了copy()的动作.
这样才能防止如果obj2是obj1的浅拷贝, 很容易出现array的元素有可能被过早释放的情况.
这里我们还可以一并说下CCZone这个类, 这个类也很简单, 只有一个构造函数和成员变量.
CCZone1 2 3 4 5 6 7 8
| class CC_DLL CCZone { public: CCZone(CCObject *pObject = NULL); public: CCObject *m_pCopyObject; };
|
我想CCZone存在的目的就是为了调用copyWithZone的时候对象传递方便.比如我们还可以看下CCSpeed的copyWithZone函数.
CCSpeed1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| CCObject *CCSpeed::copyWithZone(CCZone *pZone) { CCZone* pNewZone = NULL; CCSpeed* pRet = NULL; if(pZone && pZone->m_pCopyObject) { pRet = (CCSpeed*)(pZone->m_pCopyObject); } else { pRet = new CCSpeed(); pZone = pNewZone = new CCZone(pRet); } CCAction::copyWithZone(pZone); pRet->initWithAction( (CCActionInterval*)(m_pInnerAction->copy()->autorelease()) , m_fSpeed ); CC_SAFE_DELETE(pNewZone); return pRet; }
|
我们可以看到构建了一个pZone, 然后调用父类的CCAction::copyWithZone(pZone);
.
完全是为了copyWithZone方便传递变量而创建的.
拷贝机制看起来有点复杂, 但其实用起来很简单. 我觉得就两点, 1,使用copy()调用 2.各个类对自己的copyWithZone负责.
##内存管理
cocos2d-x的内存管理采用了引用计数的方法. 曾经看到过有人吐槽其内存管理在多线程下不好用.
CCObject及其子类的对象在创建时, 引用计数默认为1, 每次retain后引用计数加1. 每次release后引用计数减1.
被自动管理的对象引用计数为0时, 会被自动释放.
老G总结的内存管理使用的几点原则(见参考1):
- 原则1: 谁生成(new, copy)谁负责release.
- 原则2: 谁retain, 谁负责release.
- 原则3: 对于使用了autorelease的对象则不必管他.
##几个常用的函数指针
当我看到下面这两句的时候, 我完全懵了.
typedef void (CCObject::*SEL_SCHEDULE)(float);
#define schedule_selector(_SELECTOR) (SEL_SCHEDULE)(&_SELECTOR)
如果你也对函数指针不熟悉的话, 请翻下相关内容吧.
在这两句里面, 第一句其实是定义了一个返回类型为void的, 名字为SEL_SCHEDULE的, 参数为float的函数指针.
第二句其实是一个函数类型转换, 将(&_SELECTOR)强制转换成SEL_SCHEDULE类型的函数指针.
通常需要回调函数的时候, 就需要用到这些函数指针. 这里就不再赘述, 以后应该会提及相关知识.
好, 我们接下来直接翻代码吧.
- 版本:
cocos2d-2.1beta3-x-2.1.0
- 路径:
cocos2d-2.1beta3-x-2.1.0/cocos2dx/cocoa/CCObject.h
- 路径:
cocos2d-2.1beta3-x-2.1.0/cocos2dx/cocoa/CCObject.cpp
CCobject\.hview raw1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| class CC_DLL CCCopying { public: virtual CCObject* copyWithZone(CCZone* pZone); }; class CC_DLL CCObject : public CCCopying { public: unsigned int m_uID; int m_nLuaID; protected: unsigned int m_uReference; unsigned int m_uAutoReleaseCount; public: CCObject(void); virtual ~CCObject(void); void release(void); void retain(void); CCObject* autorelease(void); CCObject* copy(void); bool isSingleReference(void); unsigned int retainCount(void); virtual bool isEqual(const CCObject* pObject); virtual void update(float dt) {CC_UNUSED_PARAM(dt);}; friend class CCAutoreleasePool; }; typedef void (CCObject::*SEL_SCHEDULE)(float); typedef void (CCObject::*SEL_CallFunc)(); typedef void (CCObject::*SEL_CallFuncN)(CCNode*); typedef void (CCObject::*SEL_CallFuncND)(CCNode*, void*); typedef void (CCObject::*SEL_CallFuncO)(CCObject*); typedef void (CCObject::*SEL_MenuHandler)(CCObject*); typedef void (CCObject::*SEL_EventHandler)(CCEvent*); typedef int (CCObject::*SEL_Compare)(CCObject*); #define schedule_selector(_SELECTOR) (SEL_SCHEDULE)(&_SELECTOR) #define callfunc_selector(_SELECTOR) (SEL_CallFunc)(&_SELECTOR) #define callfuncN_selector(_SELECTOR) (SEL_CallFuncN)(&_SELECTOR) #define callfuncND_selector(_SELECTOR) (SEL_CallFuncND)(&_SELECTOR) #define callfuncO_selector(_SELECTOR) (SEL_CallFuncO)(&_SELECTOR) #define menu_selector(_SELECTOR) (SEL_MenuHandler)(&_SELECTOR) #define event_selector(_SELECTOR) (SEL_EventHandler)(&_SELECTOR) #define compare_selector(_SELECTOR) (SEL_Compare)(&_SELECTOR)
|
CCobject\.cppview raw1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| CCObject* CCCopying::copyWithZone(CCZone *pZone) { CC_UNUSED_PARAM(pZone); CCAssert(0, "not implement"); return 0; } CCObject::CCObject(void) :m_uAutoReleaseCount(0) ,m_uReference(1) ,m_nLuaID(0) { static unsigned int uObjectCount = 0; m_uID = ++uObjectCount; } CCObject::~CCObject(void) { if (m_uAutoReleaseCount > 0) { CCPoolManager::sharedPoolManager()->removeObject(this); } if (m_nLuaID) { CCScriptEngineManager::sharedManager()->getScriptEngine()->removeScriptObjectByCCObject(this); } else { CCScriptEngineProtocol* pEngine = CCScriptEngineManager::sharedManager()->getScriptEngine(); if (pEngine != NULL && pEngine->getScriptType() == kScriptTypeJavascript) { pEngine->removeScriptObjectByCCObject(this); } } } CCObject* CCObject::copy() { return copyWithZone(0); } void CCObject::release(void) { CCAssert(m_uReference > 0, "reference count should greater than 0"); --m_uReference; if (m_uReference == 0) { delete this; } } void CCObject::retain(void) { CCAssert(m_uReference > 0, "reference count should greater than 0"); ++m_uReference; } CCObject* CCObject::autorelease(void) { CCPoolManager::sharedPoolManager()->addObject(this); return this; } bool CCObject::isSingleReference(void) { return m_uReference == 1; } unsigned int CCObject::retainCount(void) { return m_uReference; } bool CCObject::isEqual(const CCObject *pObject) { return this == pObject; }
|