cocos2d-x之__Array:: createWithContentsOfFile引發的“血案”

***************************************************************************

時間:2014-11-8

作者:Sharing_Li

轉載出處:http://blog.csdn.net/sharing_li/article/details/40895213

***************************************************************************

 

      昨天寫代碼,想從一個plist文件中讀取一個數組,如下所示:

<?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">
<array>
	<dict>
		<key>x</key>
		<integer>12</integer>
		<key>y</key>
		<integer>20</integer>
	</dict>
	<dict>
		<key>x</key>
		<integer>17</integer>
		<key>y</key>
		<integer>25</integer>
	</dict>
</array>
</plist>

      用的方法是cocos2d-x3.2版本的__Array::createWithContentsOfFile("xxxxx.plist");結果讀取失敗,程序一運行直接崩潰,提示什麼斷言錯誤。一開始以爲自己的plist文件格式不對,但一查資料,發現格式是對的,沒什麼問題。網上看了下別人用的2.x版本也沒什麼問題,然後對比了下2.x和3.x的數據存儲結構,終於發現了問題的所在。

      在2.x版本中,使用CCArray可以保存不同數據類型的數據,使用createWithContentsOfFile生成Array沒有問題。但是到了3.x版本中,用cocos2d::Vector替代了CCArray的功能,cocos2d::Vector<T>中的T必須是一個指向cocos2d::Ref子類對象的指針,不能是其他數據類型或者是原生類型(即基本類型),因爲開發人員已經將Cocos2dx的內存管理模型集成到cocos2d::Vector<T>中,並且Vector自己並不是Ref的子類,所以不能對它的實例使用retain和release。在3.x版本中雖然有__Array,但已被棄用了,只保留了CCArray的部分功能。

      以前面的__Array::createWithContentsOfFile("xxxxx.plist");爲例,我們按F12跟蹤查看源代碼:

__Array* __Array::createWithContentsOfFile(const std::string& fileName)
{
    __Array* ret = __Array::createWithContentsOfFileThreadSafe(fileName);
    if (ret != nullptr)
    {
        ret->autorelease();
    }
    return ret;
}

__Array* __Array::createWithContentsOfFileThreadSafe(const std::string& fileName)
{
    ValueVector arr = FileUtils::getInstance()->getValueVectorFromFile(fileName);
    
    __Array* ret = __Array::createWithCapacity(static_cast<int>(arr.size()));

    for(const auto &value : arr) {
        ret->addObject(__String::create(value.asString()));
    }
    
    return ret;
}

       之前崩潰的原因是在於這一句代碼:ret->addObject(__String::create(value.asString()));因爲我們的plist文件裏面沒有string類型,而這裏好像只能讀取string類型,所以讀取失敗。我們讀取的plist文件中可以看到有int等基本類型,他不是Ref的子類,所以不能用cocos2d::Vector方法,並且Vector也沒有提供createWithContentsOfFile方法。那麼既然__Array和Vector都不行,那怎麼解決呢?

       放心,引擎給我們提供了保存非Ref子類的容器類,就是ValueVector,它在文檔中如下定義:

typedef std::vector<Value> ValueVector;

       可以看到,ValueVector實際上就是一個存放Value類型元素的std::vector容器。在3.2中,我們可以用如下方法讀取plist:

ValueVector val = FileUtils::getInstance()->getValueVectorFromFile("star.plist");
for(auto ref : val) 
{
	//將val 裏面讀取的對象ref 轉化爲ValueMap 類型
	auto temp_map = ref.asValueMap();
	//轉化之後的Val 對象 temp_map 取出x,y值
	auto x = temp_map.at("x").asInt();
	auto y = temp_map.at("y").asInt();
}

 

       不過用ValueVector讀取的plist文件只侷限於是該plist的格式的以array數組類型開頭的,例如下面這種:

<array>  
    <dict>  
        <key>name</key>  
        <string>star</string>  
        <key>number</key>  
        <integer>yes</integer>  
    </dict>  
</array>

       如果是以dict字典類型開頭的文件,則要換用ValueMap。

拓展:

       1、從前面的討論當中,我們不難得出:ValueVector中可以放ValueVector,前提是將ValueVector轉成Value類型;而Vector中不能存放Vector類型的元素。(不知道爲什麼的可以再重頭看一遍)。

       2、另外在查看ValueVector源碼時發現的一個需要注意的地方,刪除ValueVector中的元素有兩種方法:

        A、val.pop_back();直接刪除容器中最後一個元素。

        B、用erase刪除容器中的某一個元素,使用的方法是:

             auto iter = std::find(val.begin(),val.end(),x);  
             val.erase(iter);
             而不是star_val.erase(x);(鄙人曾經就放過這個錯誤)因爲erase的參數是一個迭代器,更深層的原因是Value沒有重載==運算符,而std::find裏面的數據類型必須實現==運算符,否則就不能查找,那麼也就不能刪除元素了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章