JSONModel:NSUInteger類型屬性可能的崩潰分析及解決

預(yù)備知識
當(dāng)屬性是基本數(shù)據(jù)類型時,在使用KVC時,KVC方法會自動把傳入的value對象類型轉(zhuǎn)換成基礎(chǔ)數(shù)據(jù)類型,(即調(diào)用那些intValue啥的)如果傳入的value對象類型沒有這些方法,比如你傳入一個數(shù)組對象,那么在轉(zhuǎn)換成基礎(chǔ)數(shù)據(jù)類型時將崩潰.其他轉(zhuǎn)換是JSONModel做了,比如value是NSString類型,而你的模型屬性是NSNumber類型.

遇到的bug

在項目中建了一個Model,里面有一個字段設(shè)置為NSUInteger,對應(yīng)服務(wù)器端的類型是字符型的.在4s i0S9以下正常,在4s iOS9及在5s上崩潰.
崩潰的地方出現(xiàn)在KVC賦值時.
在這里崩潰.

 // 0) handle primitives
 if (property.type == nil && property.structName==nil) {
 
 //generic setter
 if (jsonValue != [self valueForKey:property.name]) {
 [self setValue:jsonValue forKey: property.name];//在這里崩潰.崩潰提示:-[NSTaggedPointerString unsignedLongLongValue]: unrecognized selector sent to instance 0xa000000000033322.顯然是因為NSString類里面沒有unsignedLongLongValue這個方法.
 }
 
 //skip directly to the next key
 continue;
 }

那么為什么在4s i0S9以下沒有崩潰而在4s iOS9及在5s以上崩潰呢?
極有可能是因為在4s i0S9以下時,系統(tǒng)能夠找到將NSString對象轉(zhuǎn)為基礎(chǔ)類型NSUInteger的方法,而在4s iOS9及在5s以上時找不到對應(yīng)的方法從而拋出unrecognized selector錯誤.現(xiàn)在要做的就是找到在4s i0S9以下時系統(tǒng)調(diào)用了NSString的哪個方法.想要看執(zhí)行了哪個方法最簡單的辦法就是打斷點(diǎn)了,然而系統(tǒng)的源代碼看都看不到怎么斷點(diǎn)?沒關(guān)系,通過符號斷點(diǎn)就可以斷住了.

根據(jù)宏定義:

#if __LP64__ || (TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE) || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64
typedef long NSInteger;
typedef unsigned long NSUInteger;
#else
typedef int NSInteger;
typedef unsigned int NSUInteger;
#endif

可知在4s i0S9以下時的NSUInteger相當(dāng)于unsigned int.其他情況NSUInteger相當(dāng)于unsigned long.但是查了一下NSString類轉(zhuǎn)基礎(chǔ)類型的方法:

@property (readonly) double doubleValue;
@property (readonly) float floatValue;
@property (readonly) int intValue;
@property (readonly) NSInteger integerValue NS_AVAILABLE(10_5, 2_0);
@property (readonly) long long longLongValue NS_AVAILABLE(10_5, 2_0);
@property (readonly) BOOL boolValue NS_AVAILABLE(10_5, 2_0);  // Skips initial space characters

也沒有unsignedIntValue;方法.為啥沒崩潰呢?只能說明蘋果調(diào)用了其他的某個方法(事實(shí)證明蘋果調(diào)用了longLongValue方法).根據(jù)文檔說明:
Similarly, setValue:forKey: determines the data type required by the appropriate accessor or instance variable for the specified key. If the data type is not an object, then the value is extracted from the passed object using the appropriate -<type>Value method.
再通過打符號斷點(diǎn)"-[NSString longLongValue]",發(fā)現(xiàn)程序斷住了:

23EEA9D2-9056-4EC3-919F-39758606D63D.png

說明蘋果確實(shí)調(diào)用了longLongValue方法.

如何解決這個bug

顯然最方便的是不使用NSUInteger類型聲明屬性,直接用NSString.
第二種方法改JSONModel的源碼,在KVC時對于上述情況,先將NSString值轉(zhuǎn)換成NSNumber值再KVC.

// 0) handle primitives
if (property.type == nil && property.structName==nil) {
    //generic setter
    if (jsonValue != [self valueForKey:property.name])
    {
        id tempJsonValue = jsonValue;
        if ([jsonValue isKindOfClass:[NSString class]])
        {
            id rs = [[[NSNumberFormatter alloc] init] numberFromString:jsonValue];
            if (!rs)
            {
                rs = jsonValue;
            }
            tempJsonValue = rs;
        }
        [self setValue:tempJsonValue forKey: property.name];
    }
    //skip directly to the next key
    continue;
}

顯然改源碼比較麻煩,而且效率還降低了.

3.JSONModelClassProperty 起到很重要的作用.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容