預(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)程序斷住了:
說明蘋果確實(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 起到很重要的作用.