前言:這幾天在做cocos2dx上的跨平臺(tái)的socket庫,,看來看去也只能用BSD去實(shí)現(xiàn)了,。因?yàn)橐邮辗?wù)器的推送消息,所以要做成異步處理,。也參考了很多別人的文章,,本來是想能有個(gè)輪子直接給我用的,看來還是得自己重復(fù)造下,。目前的想法是封裝一個(gè)業(yè)務(wù)層,,內(nèi)部包含一個(gè)socket封裝,使用pthread起一個(gè)線程去select接收數(shù)據(jù),,當(dāng)有數(shù)據(jù)到達(dá)時(shí),,調(diào)用業(yè)務(wù)層的函數(shù)處理數(shù)據(jù)。業(yè)務(wù)層注冊(cè)有界面層的回調(diào)函數(shù),,然后預(yù)處理完畢后直接丟給對(duì)應(yīng)的界面回調(diào)函數(shù)處理,。但是中途編譯上遇到了問題,當(dāng)今天看了下cocos2dx內(nèi)部對(duì)應(yīng)的菜單響應(yīng)事件才搞懂原來是這樣弄的,。
其實(shí)這個(gè)問題可以簡單得看成是A類注冊(cè)了B類的回調(diào)函數(shù),,當(dāng)A類對(duì)象接收到對(duì)應(yīng)的事件后,直接調(diào)用B類的回調(diào)函數(shù)處理,。本來回調(diào)函數(shù)就是給C語言的規(guī)范,,因?yàn)榈搅薈++里面有對(duì)象對(duì)象的概念后,每個(gè)成員對(duì)象調(diào)用的時(shí)候都隱含了一個(gè)this指針,,當(dāng)然,,A類是不具有B類的回調(diào)函數(shù)實(shí)現(xiàn)的,自然編譯的時(shí)候就會(huì)有問題,。
看到網(wǎng)上也有很多解決方法,,其中最牛氣的一個(gè)就是用一個(gè)thunk解決,這個(gè)有點(diǎn)高端,,因?yàn)槭侵苯油ㄟ^匯編代碼修改EAX寄存器的值來實(shí)現(xiàn)這個(gè)this指針的問題,,怕不通用,,其實(shí)最麻煩的是對(duì)這個(gè)不熟悉,到時(shí)候出問題都不知道如何解決,,想想還是作罷,。其實(shí)開始的時(shí)候就有人提到可以把調(diào)用的this指針替換成回調(diào)函數(shù)的真正擁有者,當(dāng)時(shí)沒理解,,現(xiàn)在翻看了cocos2dx的代碼才發(fā)現(xiàn),這貨就是這么搞的,!當(dāng)時(shí)我還覺得很神奇,,原來也是這種土辦法,呵呵,。就拿CCMenuItemImage做例子好了,。
看下CCMenuItem的類定義:
- class CC_DLL CCMenuItem : public CCNodeRGBA
- {
- protected:
- /** whether or not the item is selected
- @since v0.8.2
- */
- bool m_bSelected;
- bool m_bEnabled;
-
- public:
- CCMenuItem()
- : m_bSelected(false)
- , m_bEnabled(false)
- , m_pListener(NULL)
- , m_pfnSelector(NULL)
- , m_nScriptTapHandler(0)
- {}
- virtual ~CCMenuItem();
-
- /** Creates a CCMenuItem with no target/selector */
- static CCMenuItem* create();
- /** Creates a CCMenuItem with a target/selector */
- static CCMenuItem* create(CCObject *rec, SEL_MenuHandler selector);
- /** Initializes a CCMenuItem with a target/selector */
- bool initWithTarget(CCObject *rec, SEL_MenuHandler selector);
- /** Returns the outside box */
- CCRect rect();
- /** Activate the item */
- virtual void activate();
- /** The item was selected (not activated), similar to "mouse-over" */
- virtual void selected();
- /** The item was unselected */
- virtual void unselected();
-
- /** Register menu handler script function */
- virtual void registerScriptTapHandler(int nHandler);
- virtual void unregisterScriptTapHandler(void);
- int getScriptTapHandler() { return m_nScriptTapHandler; };
-
- virtual bool isEnabled();
- //@note: It's 'setIsEnable' in cocos2d-iphone.
- virtual void setEnabled(bool value);
- virtual bool isSelected();
-
- virtual void setOpacityModifyRGB(bool bValue) {CC_UNUSED_PARAM(bValue);}
- virtual bool isOpacityModifyRGB(void) { return false;}
-
- /** set the target/selector of the menu item*/
- void setTarget(CCObject *rec, SEL_MenuHandler selector);
-
- protected:
- CCObject* m_pListener; // 回調(diào)函數(shù)擁有者
- SEL_MenuHandler m_pfnSelector; // 回調(diào)函數(shù)
- int m_nScriptTapHandler;
- };
其中有兩個(gè)很關(guān)鍵的成員變量,CCObject* m_pListener 和 SEL_MenuHandler m_pfnSelector,。來看下SEL_MenuHandler的定義:
- typedef void (CCObject::*SEL_MenuHandler)(CCObject*);
-
- #define menu_selector(_SELECTOR) (SEL_MenuHandler)(&_SELECTOR)
是的,,這個(gè)就是回調(diào)函數(shù)的類型,然后下面的宏定義是為了方便壘代碼用的,。
再來看看注冊(cè)的代碼:
- CCMenuItemImage * CCMenuItemImage::create(const char *normalImage, const char *selectedImage)
- {
- return CCMenuItemImage::create(normalImage, selectedImage, NULL, NULL, NULL);
- }
-
- CCMenuItemImage * CCMenuItemImage::create(const char *normalImage, const char *selectedImage, CCObject* target, SEL_MenuHandler selector)
- {
- return CCMenuItemImage::create(normalImage, selectedImage, NULL, target, selector);
- }
-
- CCMenuItemImage * CCMenuItemImage::create(const char *normalImage, const char *selectedImage, const char *disabledImage, CCObject* target, SEL_MenuHandler selector)
- {
- CCMenuItemImage *pRet = new CCMenuItemImage();
- if (pRet && pRet->initWithNormalImage(normalImage, selectedImage, disabledImage, target, selector))
- {
- pRet->autorelease();
- return pRet;
- }
- CC_SAFE_DELETE(pRet);
- return NULL;
- }
-
- bool CCMenuItemImage::initWithNormalImage(const char *normalImage, const char *selectedImage, const char *disabledImage, CCObject* target, SEL_MenuHandler selector)
- {
- CCNode *normalSprite = NULL;
- CCNode *selectedSprite = NULL;
- CCNode *disabledSprite = NULL;
-
- if (normalImage)
- {
- normalSprite = CCSprite::create(normalImage);
- }
-
- if (selectedImage)
- {
- selectedSprite = CCSprite::create(selectedImage);
- }
-
- if(disabledImage)
- {
- disabledSprite = CCSprite::create(disabledImage);
- }
- return initWithNormalSprite(normalSprite, selectedSprite, disabledSprite, target, selector);
- }
最后反正就是調(diào)用下面的一個(gè)實(shí)現(xiàn)回調(diào)函數(shù)注冊(cè),。
- // 后期注冊(cè)
- void CCMenuItem::setTarget(CCObject *rec, SEL_MenuHandler selector)
- {
- m_pListener = rec;
- m_pfnSelector = selector;
- }
-
- // 初期綁定
- CCMenuItem* CCMenuItem::create(CCObject *rec, SEL_MenuHandler selector)
- {
- CCMenuItem *pRet = new CCMenuItem();
- pRet->initWithTarget(rec, selector);
- pRet->autorelease();
- return pRet;
- }
-
- bool CCMenuItem::initWithTarget(CCObject *rec, SEL_MenuHandler selector)
- {
- setAnchorPoint(ccp(0.5f, 0.5f));
- m_pListener = rec;
- m_pfnSelector = selector;
- m_bEnabled = true;
- m_bSelected = false;
- return true;
- }
好了,就這么綁定起來了,,調(diào)用的時(shí)候也特簡單,,看如下代碼:
- void CCMenuItem::activate()
- {
- if (m_bEnabled)
- {
- if (m_pListener && m_pfnSelector)
- {
- (m_pListener->*m_pfnSelector)(this); // 直接調(diào)用了便是
- }
-
- if (kScriptTypeNone != m_eScriptType)
- {
- CCScriptEngineManager::sharedManager()->getScriptEngine()->executeMenuItemEvent(this);
- }
- }
- }
(m_pListener->*m_pfnSelector)(this); 這句可能要稍微解釋下,其實(shí)也挺好懂的,。首先前面這個(gè)括號(hào)就是對(duì)應(yīng)的函數(shù),,后面的this就是參數(shù),這個(gè)滿足對(duì)函數(shù)指針的定義,,即void (CCObject::*)(CCObject*); 因?yàn)镃CMenuItem基類是CCObject,,那其實(shí)調(diào)用的場景(CScene)基類也一樣。然后就是m_pListener->*m_pfnSelector這個(gè)了,,前面的Listener應(yīng)該沒啥疑議,,關(guān)鍵是*m_pfnSelector可能有些人會(huì)懵,其實(shí)也很簡單,,m_pfnSelector是函數(shù)指針,,首先他是個(gè)指針,存的是個(gè)地址,,然后*自然是取對(duì)應(yīng)地址內(nèi)的內(nèi)容(函數(shù)地址),,對(duì)于函數(shù)調(diào)用來講,其實(shí)就是調(diào)用一個(gè)地址,,而m_pListener->m_pfnSelector明顯是錯(cuò)誤的,,因?yàn)檎{(diào)用者沒有m_pfnSelector這個(gè)成員變量,,他只有對(duì)應(yīng)這個(gè)指針內(nèi)部存儲(chǔ)的函數(shù)地址對(duì)其是有效的。
順便把調(diào)用active的代碼也放一下更清楚:
- void CCMenu::ccTouchEnded(CCTouch *touch, CCEvent* event)
- {
- CC_UNUSED_PARAM(touch);
- CC_UNUSED_PARAM(event);
- CCAssert(m_eState == kCCMenuStateTrackingTouch, "[Menu ccTouchEnded] -- invalid state");
- if (m_pSelectedItem)
- {
- m_pSelectedItem->unselected();
- m_pSelectedItem->activate(); // 就是這里
- }
- m_eState = kCCMenuStateWaiting;
- }
ccTouchEnded這種反正就是響應(yīng)屏幕的操作了,,再網(wǎng)上我就不貼了,,自己找找也能找到的。
|