【玩转cocos2d-x之二十六】数据结构CCDictionary

CCDictionary在cocos2d-x中被大量的应用,比如CCTexureCache,CCSpriteFramCache等等。

实现原理

uthash

CCDictionary是使用uthash实现的,而过时的CCMutableDictionary则是使用STL实现,就效率而言CCDictionary至少提升了两倍,而且CCDictionary并没有使用C++模版,因此也很容易绑定到脚本。

uthash是一个C/C++的哈希表实现,它以宏定义的方式实现了哈希表,不仅加快了运行速度,而且与key类型无关。它的github在这里。cocos2d-x的头文件在\cocos2d-x-2.2.0\cocos2dx\support\data_support\uthash.h。

如果想在C++中直接使用也很简单,userguide在这里。可以很方便地进行增加,删除,查找,计数,迭代,排序等操作。

键(key)

uthash支持4种标准类型的键:整型,字符串,指针和结构体,不过到了CCDictionary就只支持整型和字符串型了。

CCDictType
1
2
3
4
5
6
enum CCDictType  
{
kCCDictUnknown = 0,
kCCDictStr,
kCCDictInt
};

CCDictElement

在了解CCDictionary之前还要看一下CCDictElement,很明显,CCDictElement是CCDictionary的一个元素,包含了一个key-value。key支持整型和字符串,使用的时候要注意在同一个CCDictionary中key类型必须要一致,value可以不一致。

API

创建

创建
1
2
3
4
5
6
7
8
9
//创建一个CCDictionary  
static CCDictionary* create();
//用一个已存在的CCDictionary来创建一个新的CCDictionary
static CCDictionary* createWithDictionary(CCDictionary* srcDict);
//用一个plist来创建CCDictionary
static CCDictionary* createWithContentsOfFile(const char *pFileName);
//返回一个非autorelease对象的CCDictionary,它讷讷感够确保在新线程中使用
//但是你必须手动管理它的生命周期,当你不再需要它的时候,必须调用CC_SAFE_RELEASE
static CCDictionary* createWithContentsOfFileThreadSafe(const char *pFileName);

查找

查找
1
2
3
4
5
6
7
8
//返回指定字符串类型key的value,如果CCDictionary的key是整型,会出现断言  
CCObject* objectForKey(const std::string& key);
//返回指定整型key的value,如果CCDictionary的key是字符串型,会出现断言
CCObject* objectForKey(intptr_t key);
//返回指定字符串类型key的CCString,这里假定value是CCString型,如果不是或者未找到,则返回空串
const CCString* valueForKey(const std::string& key);
//返回指定整型类型key的CCString,这里假定value是CCString型,如果不是或者未找到,则返回空串
const CCString* valueForKey(intptr_t key);

增加

增加
1
2
3
4
5
6
//插入一个key-value,如果是第一次调用,那么CCDictionary的key类型会被确定为字符串型,之后就不能插入整型key  
//如果已存在该key,则旧key-value会被释放和移除,被新的替代
void setObject(CCObject* pObject, const std::string& key);
//插入一个key-value,如果是第一次调用,那么CCDictionary的key类型会被确定为整型,之后就不能插入字符串型key
//如果已存在该key,则旧key-value会被释放和移除,被新的替代
void setObject(CCObject* pObject, intptr_t key);

移除

移除
1
2
3
4
5
6
7
8
9
//移除指定key  
void removeObjectForKey(const std::string& key);
void removeObjectForKey(intptr_t key);
//移除一个CCArray中keys
void removeObjectsForKeys(CCArray* pKeyArray);
//通过元素来移除value
void removeObjectForElememt(CCDictElement* pElement);
//移除所有的key-value
void removeAllObjects();

其他

其他
1
2
3
4
5
6
7
8
9
10
//返回一个随机元素,这个使用得注意,因为value可以不一样,所以返回类型每次都可能不同,在类型转换的时候要非常小心  
CCObject* randomObject();
//返回一个包含所有key的CCArray
CCArray* allKeys();
//返回指定value的所有key,因为value是可以相同的,内部使用==比较两个value是否相同
CCArray* allKeysForObject(CCObject* object);
//返回元素个数
unsigned int count();
//把CCDictionary写到一个plist中,写入的value要求是字符串型
bool writeToFile(const char *fullPath);

示例

示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Create a dictionary, return an autorelease object.  
CCDictionary* pDict = CCDictionary::create();

// Insert objects to dictionary
CCString* pValue1 = CCString::create("100");
CCString* pValue2 = CCString::create("120");
CCInteger* pValue3 = CCInteger::create(200);
pDict->setObject(pValue1, "key1");
pDict->setObject(pValue2, "key2");
pDict->setObject(pValue3, "key3");

// Get the object for key
CCString* pStr1 = (CCString*)pDict->objectForKey("key1");
CCLog("{ key1: %s }", pStr1->getCString());
CCInteger* pInteger = (CCInteger*)pDict->objectForKey("key3");
CCLog("{ key3: %d }", pInteger->getValue());
CCString* pStr3=static_cast<CCString*>(pDict->randomObject());//这里有问题了,因为value有不同类型,所以随机返回时类型处理要小心
CCLog("{ random key: %s }",pStr3->getCString()); //如果返回的是整型pValue3,那么会出现断言
if(pDict->writeToFile("pdic.plist")) //整型的value无法写入,会提示This type cannot appear in property list
CCLog("Write to file success!");

Resource/pdic.plist

pdic.plist
1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"/>

<plist version="1.0">
<dict>
<key>key1</key>
<string>100</string>
<key>key2</key>
<string>120</string>
<key>key3</key> <!key3对应整型数据无法写入/>
</dict>
</plist>

CCDICT_FOREACH

概况

宏定义,用于遍历CCDictionary的value。

CCDICT_FOREACH宏定义
1
2
3
4
#define CCDICT_FOREACH(__dict__, __el__) \  
CCDictElement* pTmp##__dict__##__el__ = NULL; \
if (__dict__) \
HASH_ITER(hh, (__dict__)->m_pElements, __el__, pTmp##__dict__##__el__)

示例

CCDICT_FOREACH示例
1
2
3
4
5
6
7
8
CCDictElement* pElement;  
CCDICT_FOREACH(dict, pElement)
{
const char*key = pElement->getStrKey();
// You certainly know the type of value, so we assume that it's a CCSprite.
CCSprite* pSprite = (CCSprite*)pElement->getObject();
// ......
}
文章目录
  1. 1. 实现原理
    1. 1.1. uthash
    2. 1.2. 键(key)
  2. 2. CCDictElement
  3. 3. API
    1. 3.1. 创建
    2. 3.2. 查找
    3. 3.3. 增加
    4. 3.4. 移除
    5. 3.5. 其他
  4. 4. 示例
  5. 5. CCDICT_FOREACH
    1. 5.1. 概况
    2. 5.2. 示例
,