#ifndef __CCDICTIONARY_H__ #define __CCDICTIONARY_H__ //需要哈希表的支持 #include "support/data_support/uthash.h" #include "CCObject.h" #include "CCArray.h" #include "CCString.h" //Cocos2d命名空間 NS_CC_BEGIN //聲明一下CCDictionary類,因為CCDictElement要用到CCDictionary指針。 class CCDictionary; //詞典元素,或者簡單理解就是詞典中的一個詞匯。我們從小查詞典都知道,通過詞匯名稱或索引查找到對應的解釋。解釋與詞匯名稱或索引之間是一一對應的關系。與這種關系相同,在這個詞匯類中存儲一個字符串名稱或一個索引以及與其相應的CCObject指針,這個CCObject指針就相當於是我們查出來的解釋一樣與字符串名稱或索引構成了對應關系。 class CC_DLL CCDictElement { //定義字符串名稱的長度. #define MAX_KEY_LEN 256 public: //構造函數。 //參1:字符串名稱。 //參2:對應的CCObject指針。 CCDictElement(const char* pszKey, CCObject* pObject) { //初始化。 init(); m_pObject = pObject; // const char* pStart = pszKey; //字符串的字節長度 int len = strlen(pszKey); if (len > MAX_KEY_LEN ) { //如果長度大於MAX_KEY_LEN,截取后面MAX_KEY_LEN長度字符串。 char* pEnd = (char*)&pszKey[len-1]; pStart = pEnd - (MAX_KEY_LEN-1); } //字符串COPY strcpy(m_szKey, pStart); } //構造函數 //參1:所在哈希表中的索引 //參2:對應的CCObject指針。 CCDictElement(int iKey, CCObject* pObject) { init(); m_iKey = iKey; m_pObject = pObject; } //取得名稱字符串。 inline const char* getStrKey() const { CCAssert(m_szKey[0] != '\0', "Should not call this function for integer dictionary"); return m_szKey; } //取得哈希索引。 inline int getIntKey() const { CCAssert(m_szKey[0] == '\0', "Should not call this function for string dictionary"); return m_iKey; } //取得CCObject指針。 inline CCObject* getObject() const { return m_pObject; } private: //初始化。 inline void init() { m_iKey = 0; m_pObject = NULL; memset(m_szKey, 0, sizeof(m_szKey)); memset(&hh, 0, sizeof(hh)); } private: char m_szKey[MAX_KEY_LEN+1]; //存儲名稱的字符數組。 int m_iKey; //哈希表索引 CCObject* m_pObject; //哈希值(CCObject指針) public: UT_hash_handle hh; //哈希表結構指針 friend class CCDictionary; //詞典為友元類 }; //遍歷詞典中的所有詞匯的一個宏,它內部調用HASH_ITER來進行for循環遍歷鏈表。 #define CCDICT_FOREACH(__dict__, __el__) \ CCDictElement* pTmp##__dict__##__el__ = NULL; \ HASH_ITER(hh, (__dict__)->m_pElements, __el__, pTmp##__dict__##__el__) //詞典類,由CCObject派生 class CC_DLL CCDictionary : public CCObject { public: //構造函數 CCDictionary(); //析構函數 ~CCDictionary(); //取得所有詞匯的數量。 unsigned int count(); //返回所有的查詢關鍵字。 CCArray* allKeys(); //取得對應CCObject指針的所有關鍵字或索引值。 CCArray* allKeysForObject(CCObject* object); //通過查詢關鍵字取得對應CCObject指針 CCObject* objectForKey(const std::string& key); //通過哈希索引值取得對應CCObject指針 CCObject* objectForKey(int key); //通過查詢關鍵字取得對應CCString指針 const CCString* valueForKey(const std::string& key); //通過哈希索引值取得對應CCString指針 const CCString* valueForKey(int key); //設置一個CCObject和對應的名稱存入詞典。 void setObject(CCObject* pObject, const std::string& key); //設置一個CCObject和對應的哈希索引存入詞典。 void setObject(CCObject* pObject, int key); //按照查詢關鍵字找到對應CCObject並刪除。 void removeObjectForKey(const std::string& key); //按照哈希索引找到對應CCObject並刪除。 void removeObjectForKey(int key); //按照容器中的查詢關鍵字找到對應CCObject並刪除。 void removeObjectsForKeys(CCArray* pKeyArray); //從詞典中刪除相應的詞匯。 void removeObjectForElememt(CCDictElement* pElement); //從詞典中清空所有的詞匯。 void removeAllObjects(); //重載CCObject的拷貝函數。產生一個一模一樣的詞典。 virtual CCObject* copyWithZone(CCZone* pZone); //靜態函數,取得單例的詞典。請改用create函數,因為這個函數以后將被刪除掉。 CC_DEPRECATED_ATTRIBUTE static CCDictionary* dictionary(); //靜態函數,取得一個指定詞典的COPY,請改用createWithDictionary函數,因為這個函數以后將被刪除掉。 CC_DEPRECATED_ATTRIBUTE static CCDictionary* dictionaryWithDictionary(CCDictionary* srcDict); //靜態函數:從一個plist文件中加載詞典內容。此函數創建的詞典是交由內存管理器來進行資源計數的,不需手動release。但請改用createWithContentsOfFile函數,因為這個函數以后也將被刪除掉。 CC_DEPRECATED_ATTRIBUTE static CCDictionary* dictionaryWithContentsOfFile(const char *pFileName); //靜態函數:從一個plist文件中加載詞典內容,但此函數是多線程安全的,另外此函數創建的詞典需要手動release。請改用 createWithContentsOfFileThreadSafe函數,因為這個函數以后也將被刪除掉。 CC_DEPRECATED_ATTRIBUTE static CCDictionary* dictionaryWithContentsOfFileThreadSafe(const char *pFileName); //靜態函數,創建一個新詞典 static CCDictionary* create(); //靜態函數,取得一個指定詞典的COPY。 static CCDictionary* createWithDictionary(CCDictionary* srcDict); //靜態函數:從一個plist文件中加載詞典內容。 static CCDictionary* createWithContentsOfFile(const char *pFileName); //靜態函數:從一個plist文件中加載詞典內容,但此函數是多線程安全的,另外此函數創建的詞典需要手動release。 static CCDictionary* createWithContentsOfFileThreadSafe(const char *pFileName); private: //將CCObject實例指針與對應的字符串名稱存入哈希表。 void setObjectUnSafe(CCObject* pObject, const std::string& key); //將CCObject實例指針與對應的索引值存入哈希表。 void setObjectUnSafe(CCObject* pObject, const int key); public: //詞匯的哈希表頭部結構指針。 CCDictElement* m_pElements; private: //詞典查詢類型。 enum CCDictType { kCCDictUnknown = 0, kCCDictStr,//字符串名稱 kCCDictInt //索引 }; CCDictType m_eDictType; //當前詞典查詢類型。一個詞典實例要求只有一種固定詞典查詢類型。 CCDictType m_eOldDictType; //上次詞典查詢類型。這個變量是用來比對是否改變了詞典查詢類型。 }; NS_CC_END
#include "CCDictionary.h" #include "CCString.h" #include "CCInteger.h" using namespace std; //使用Cocos2d命名空間 NS_CC_BEGIN //構造函數 CCDictionary::CCDictionary() : m_pElements(NULL) , m_eDictType(kCCDictUnknown) , m_eOldDictType(kCCDictUnknown) { } //析構函數。 CCDictionary::~CCDictionary() { //請空詞匯,釋放所有詞匯占用的內存。 removeAllObjects(); } //取得詞典中的所有詞匯數量。 unsigned int CCDictionary::count() { //通過HASH_CONT宏來取得哈希表的元素數量。 return HASH_COUNT(m_pElements); } //返回所有的查詢關鍵字。 CCArray* CCDictionary::allKeys() { //取得詞匯的數量 int iKeyCount = this->count(); if (iKeyCount <= 0) return NULL; //創建一個iKeyCount大小的CCArray CCArray* pArray = CCArray::createWithCapacity(iKeyCount); //定義臨時詞匯指針變量。 CCDictElement *pElement, *tmp; if (m_eDictType == kCCDictStr) { //如果當前詞典查詢類型是通過名稱字符串。 //遍歷所有詞匯。 HASH_ITER(hh, m_pElements, pElement, tmp) { //取得每一個詞匯的名稱字符串放入CCArray中。 CCString* pOneKey = new CCString(pElement->m_szKey); pOneKey->autorelease(); pArray->addObject(pOneKey); } } else if (m_eDictType == kCCDictInt) { //如果當前詞典查詢類型是通過索引。 //遍歷所有詞匯。 HASH_ITER(hh, m_pElements, pElement, tmp) { //取得每一個詞匯的名稱字符串放入CCArray中。 CCInteger* pOneKey = new CCInteger(pElement->m_iKey); pOneKey->autorelease(); pArray->addObject(pOneKey); } } return pArray; } //取得對應CCObject指針的所有關鍵字或索引值。 CCArray* CCDictionary::allKeysForObject(CCObject* object) { //取得詞匯的數量 int iKeyCount = this->count(); if (iKeyCount <= 0) return NULL; //創建一個CCArray。 CCArray* pArray = CCArray::create(); //定義臨時詞匯指針變量。 CCDictElement *pElement, *tmp; if (m_eDictType == kCCDictStr) { //如果當前詞典查詢類型是通過名稱字符串。 //遍歷所有詞匯。 HASH_ITER(hh, m_pElements, pElement, tmp) { if (object == pElement->m_pObject) { //如果與指定的詞匯相同,將其名稱字符串放入CCArray中。 CCString* pOneKey = new CCString(pElement->m_szKey); pArray->addObject(pOneKey); pOneKey->release(); } } } else if (m_eDictType == kCCDictInt) { //如果當前詞典查詢類型是通過索引。 //遍歷所有詞匯。 HASH_ITER(hh, m_pElements, pElement, tmp) { //如果與指定的詞匯相同,將其名稱字符串放入CCArray中。 if (object == pElement->m_pObject) { CCInteger* pOneKey = new CCInteger(pElement->m_iKey); pArray->addObject(pOneKey); pOneKey->release(); } } } return pArray; } //通過查詢關鍵字取得對應CCObject指針 CCObject* CCDictionary::objectForKey(const std::string& key) { //當前詞典查詢類型值有效性判斷。此處有錯,應該改為:if (m_eDictType == kCCDictUnknown || m_eDictType == kCCDictInt) return NULL; if (m_eDictType == kCCDictUnknown && m_eDictType == kCCDictUnknown) return NULL; //要求當前詞典查詢類型為按字符串查詢。 CCAssert(m_eDictType == kCCDictStr, "this dictionary does not use string as key."); //定義臨時詞匯指針變量。 CCObject* pRetObject = NULL; CCDictElement *pElement = NULL; //通過名稱字符串查詢哈希表中的相應詞匯 HASH_FIND_STR(m_pElements, key.c_str(), pElement); if (pElement != NULL) { //如果查詢到詞匯,返回其對應的CCObject指針 pRetObject = pElement->m_pObject; } return pRetObject; } //通過查詢索引取得對應CCObject指針 CCObject* CCDictionary::objectForKey(int key) { //當前詞典查詢類型值有效性判。此處有錯,應該改為:if (m_eDictType == kCCDictUnknown || m_eDictType == kCCDictStr) return NULL; if (m_eDictType == kCCDictUnknown && m_eDictType == kCCDictUnknown) return NULL; //要求當前詞典查詢類型為按字符串查詢。 CCAssert(m_eDictType == kCCDictInt, "this dictionary does not use integer as key."); //定義臨時詞匯指針變量。 CCObject* pRetObject = NULL; CCDictElement *pElement = NULL; //通過索引查詢哈希表中的相應詞匯 HASH_FIND_INT(m_pElements, &key, pElement); if (pElement != NULL) { //如果查詢到詞匯,返回其對應的CCObject指針 pRetObject = pElement->m_pObject; } return pRetObject; } //通過查詢關鍵字取得對應CCString指針,其實即要求存入詞匯的CCObject指針是CCString實例對象指針。 const CCString* CCDictionary::valueForKey(const std::string& key) { //將通過查詢關鍵字取得對應CCString指針強轉為CCString指針。 CCString* pStr = (CCString*)objectForKey(key); if (pStr == NULL) { //如果沒找到,返回空字符串 pStr = CCString::create(""); } return pStr; } //通過查詢索引取得對應CCString指針,即要求存入詞匯的CCObject指針是CCString實例對象指針。 const CCString* CCDictionary::valueForKey(int key) { //將通過查詢索引取得對應CCString指針強轉為CCString指針。 CCString* pStr = (CCString*)objectForKey(key); if (pStr == NULL) { //如果沒找到,返回空字符串 pStr = CCString::create(""); } return pStr; } //設置一個CCObject和對應的名稱存入詞典。 void CCDictionary::setObject(CCObject* pObject, const std::string& key) { //參數有效性判斷 CCAssert(key.length() > 0 && pObject != NULL, "Invalid Argument!"); //如果是第一次存入,記錄查詢類型為字符串類型。 if (m_eOldDictType == kCCDictUnknown) { m_eOldDictType = kCCDictStr; } //將當前詞典查詢類型設為字符串查詢類型。這個變量是可以省略的,因為要求詞典查詢類型為固定。只用m_eOldDictType就可以了。 m_eDictType = kCCDictStr; CCAssert(m_eDictType == m_eOldDictType, "this dictionary does not use string as key."); //定義臨時指針變量從詞典中取得對應名稱的詞匯。 CCDictElement *pElement = NULL; HASH_FIND_STR(m_pElements, key.c_str(), pElement); if (pElement == NULL) { //如果詞典中沒有此詞匯,將此新詞匯放入詞典。 setObjectUnSafe(pObject, key); } else if (pElement->m_pObject != pObject) { //如果詞典中已有此詞匯,則刪除老詞匯放入新詞匯。 CCObject* pTmpObj = pElement->m_pObject; //此處調用retain對引用計數器加1可以避免在后面的刪除函數中釋放pTmpObj指向的CCObject。 pTmpObj->retain(); //刪除此詞匯 removeObjectForElememt(pElement); //放入新詞匯。 setObjectUnSafe(pObject, key); //因為之前retain對引用計數器加1一次,所以必須release對引用計數器減1一次才能保證由內存管理器來進行內存釋放時,pTempObj指向的CCObject可以正確的被釋放掉。 pTmpObj->release(); } } //設置一個CCObject和對應的哈希索引存入詞典。 void CCDictionary::setObject(CCObject* pObject, int key) { //參數有效性判斷 CCAssert(pObject != NULL, "Invalid Argument!"); //如果是第一次存入,記錄查詢類型為索引類型。 if (m_eOldDictType == kCCDictUnknown) { m_eOldDictType = kCCDictInt; } //將當前詞典查詢類型設為索引查詢類型。這個變量是可以省略的,因為要求詞典查詢類型為固定。只用m_eOldDictType就可以了。 m_eDictType = kCCDictInt; //一致性判斷 CCAssert(m_eDictType == m_eOldDictType, "this dictionary does not use integer as key."); //定義臨時指針變量從詞典中取得對應名稱的詞匯。 CCDictElement *pElement = NULL; HASH_FIND_INT(m_pElements, &key, pElement); if (pElement == NULL) { //如果詞典中沒有此詞匯,將此新詞匯放入詞典。 setObjectUnSafe(pObject, key); } else if (pElement->m_pObject != pObject) { //如果詞典中已有此詞匯,則刪除老詞匯放入新詞匯。 CCObject* pTmpObj = pElement->m_pObject; //此處調用retain對引用計數器加1可以避免在后面的刪除函數中釋放pTmpObj指向的CCObject。 pTmpObj->retain(); //刪除此詞匯 removeObjectForElememt(pElement); //放入新詞匯。 setObjectUnSafe(pObject, key); //因為之前retain對引用計數器加1一次,所以必須release對引用計數器減1一次才能保證由內存管理器來進行內存釋放時,pTempObj指向的CCObject可以正確的被釋放掉。 pTmpObj->release(); } } //按照查詢關鍵字找到對應CCObject並刪除。 void CCDictionary::removeObjectForKey(const std::string& key) { //當前詞典是否有效 if (m_eOldDictType == kCCDictUnknown) { return; } //當前詞典的查詢類型是否為字符串名稱查詢方式 CCAssert(m_eDictType == kCCDictStr, "this dictionary does not use string as its key"); //參數有效性判斷 CCAssert(key.length() > 0, "Invalid Argument!"); //定義臨時指針變量從詞典中取得對應名稱的詞匯。 CCDictElement *pElement = NULL; HASH_FIND_STR(m_pElements, key.c_str(), pElement); //從詞典中刪除相應的詞匯。 removeObjectForElememt(pElement); } void CCDictionary::removeObjectForKey(int key) { //當前詞典是否有效 if (m_eOldDictType == kCCDictUnknown) { return; } //當前詞典的查詢類型是否為索引查詢方式 CCAssert(m_eDictType == kCCDictInt, "this dictionary does not use integer as its key"); //定義臨時指針變量從詞典中取得對應索引的詞匯。 CCDictElement *pElement = NULL; HASH_FIND_INT(m_pElements, &key, pElement); //從詞典中刪除相應的詞匯。 removeObjectForElememt(pElement); } //將CCObject實例指針與對應的字符串名稱存入哈希表。 void CCDictionary::setObjectUnSafe(CCObject* pObject, const std::string& key) { //對pObject指向的實例對像引用計數器加1,即告訴其被詞典使用.避免萬一其的其它使用者都不再使用時被內存管理器釋放. pObject->retain(); //由pObject和名稱字符串產生一個新的詞匯。 CCDictElement* pElement = new CCDictElement(key.c_str(), pObject); //將新的詞匯放入哈希表中。 HASH_ADD_STR(m_pElements, m_szKey, pElement); } //將CCObject實例指針與對應的索引存入哈希表。 void CCDictionary::setObjectUnSafe(CCObject* pObject, const int key) { //對pObject指向的實例對像引用計數器加1,即告訴其被詞典使用.避免萬一其的其它使用者都不再使用時被內存管理器釋放. pObject->retain(); //由pObject和名稱字符串產生一個新的詞匯。 CCDictElement* pElement = new CCDictElement(key, pObject); //將新的詞匯放入哈希表中。 HASH_ADD_INT(m_pElements, m_iKey, pElement); } //按照容器中的查詢關鍵字找到對應CCObject並刪除。 void CCDictionary::removeObjectsForKeys(CCArray* pKeyArray) { //遍歷CCArray實例對像的所有名稱字符串,查詢與之對應的詞匯。並刪除。 CCObject* pObj = NULL; CCARRAY_FOREACH(pKeyArray, pObj) { CCString* pStr = (CCString*)pObj; removeObjectForKey(pStr->getCString()); } } //從詞典中刪除相應的詞匯。 void CCDictionary::removeObjectForElememt(CCDictElement* pElement) { //參數有效性判斷 if (pElement != NULL) { //從哈希表中刪除pElement指向的詞匯 HASH_DEL(m_pElements, pElement); //前面在將詞匯加入詞典時對引用計數器加1,這里刪除詞匯是就應該對引用計數器減1。 pElement->m_pObject->release(); //釋放詞匯 CC_SAFE_DELETE(pElement); } } //從詞典中清空所有的詞匯。 void CCDictionary::removeAllObjects() { //定義遍歷哈希表所用的指針變量 CCDictElement *pElement, *tmp; //遍歷哈希表 HASH_ITER(hh, m_pElements, pElement, tmp) { //刪除詞匯並釋放 HASH_DEL(m_pElements, pElement); pElement->m_pObject->release(); CC_SAFE_DELETE(pElement); } } //重載CCObject的拷貝函數。產生一個一模一樣的詞典。 CCObject* CCDictionary::copyWithZone(CCZone* pZone) { //參數有效性判斷 CCAssert(pZone == NULL, "CCDirctionary should not be inherited."); //創建一個新的詞典 CCDictionary* pNewDict = new CCDictionary(); //定義用來遍歷的臨時變量 CCDictElement* pElement = NULL; CCObject* pTmpObj = NULL; //如果是索引查詢方式 if (m_eDictType == kCCDictInt) { //遍歷所有詞匯 CCDICT_FOREACH(this, pElement) { //產生遍歷詞匯對應的CCObject的COPY,生成新的詞匯放入新的詞典中. pTmpObj = pElement->getObject()->copy(); pNewDict->setObject(pTmpObj, pElement->getIntKey()); pTmpObj->release(); } } else if (m_eDictType == kCCDictStr) { //如果是名稱字符串查詢方式. //遍歷所有詞匯 CCDICT_FOREACH(this, pElement) { //產生遍歷詞匯對應的CCObject的COPY,生成新的詞匯放入新的詞典中. pTmpObj = pElement->getObject()->copy(); pNewDict->setObject(pTmpObj, pElement->getStrKey()); pTmpObj->release(); } } return pNewDict; } //靜態函數,取得單例的詞典,內部調用create函數。 CCDictionary* CCDictionary::dictionary() { return CCDictionary::create(); } //靜態函數,取得單例的詞典。 CCDictionary* CCDictionary::create() { //創建一個新的詞典 CCDictionary* pRet = new CCDictionary(); if (pRet != NULL) { //將其設為由引用計數器來判斷釋放時機.交由內存管理器進行管理. pRet->autorelease(); } //返回新創建的詞典指針 return pRet; } //靜態函數,取得一個指定詞典的COPY,內部調用createWithDictionary函數. CCDictionary* CCDictionary::dictionaryWithDictionary(CCDictionary* srcDict) { return CCDictionary::createWithDictionary(srcDict); } //靜態函數,取得一個指定詞典的COPY. CCDictionary* CCDictionary::createWithDictionary(CCDictionary* srcDict) { //查生一個指定詞典的COPY. CCDictionary* pNewDict = (CCDictionary*)srcDict->copy(); pNewDict->autorelease(); return pNewDict; } //聲明靜態函數:從一個plist文件中加載詞典內容,此函數是多線程安全的,其內部調用 createWithContentsOfFileThreadSafe函數。 extern CCDictionary* ccFileUtils_dictionaryWithContentsOfFileThreadSafe(const char *pFileName); //靜態函數:從一個plist文件中加載詞典內容,此函數是多線程安全的. CCDictionary* CCDictionary::dictionaryWithContentsOfFileThreadSafe(const char *pFileName) { return CCDictionary::createWithContentsOfFileThreadSafe(pFileName); } //靜態函數:從一個plist文件中加載詞典內容,此函數是多線程安全的. CCDictionary* CCDictionary::createWithContentsOfFileThreadSafe(const char *pFileName) { //這里調用Cocos2d-x的文件函數集中的帶多線程安全的從plist文件加載詞典函數實現相應功能. return ccFileUtils_dictionaryWithContentsOfFileThreadSafe(pFileName); } //靜態函數:從一個plist文件中加載詞典內容,其內部調用 createWithContentsOfFile函數。 CCDictionary* CCDictionary::dictionaryWithContentsOfFile(const char *pFileName) { return CCDictionary::createWithContentsOfFile(pFileName); } //靜態函數:從一個plist文件中加載詞典內容. CCDictionary* CCDictionary::createWithContentsOfFile(const char *pFileName) { CCDictionary* pRet = createWithContentsOfFileThreadSafe(pFileName); pRet->autorelease(); return pRet; } NS_CC_END