YYKit 源碼解析(6)-YYModel

上面一篇文章沒有分析完yymodel 。

接著上篇接著分析

static void ModelSetValueForProperty(__unsafe_unretained id model,

__unsafe_unretained id value,

__unsafe_unretained _YYModelPropertyMeta *meta) {

if (meta->_isCNumber) {

NSNumber *num = YYNSNumberCreateFromID(value);

ModelSetNumberToProperty(model, num, meta);

if (num != nil) [num class]; // hold the number

} else if (meta->_nsType) {

if (value == (id)kCFNull) {

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)nil);

} else {

switch (meta->_nsType) {

case YYEncodingTypeNSString:

case YYEncodingTypeNSMutableString: {

if ([value isKindOfClass:[NSString class]]) {

if (meta->_nsType == YYEncodingTypeNSString) {

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);

} else {

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, ((NSString *)value).mutableCopy);

}

} else if ([value isKindOfClass:[NSNumber class]]) {

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,

meta->_setter,

(meta->_nsType == YYEncodingTypeNSString) ?

((NSNumber *)value).stringValue :

((NSNumber *)value).stringValue.mutableCopy);

} else if ([value isKindOfClass:[NSData class]]) {

NSMutableString *string = [[NSMutableString alloc] initWithData:value encoding:NSUTF8StringEncoding];

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, string);

} else if ([value isKindOfClass:[NSURL class]]) {

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,

meta->_setter,

(meta->_nsType == YYEncodingTypeNSString) ?

((NSURL *)value).absoluteString :

((NSURL *)value).absoluteString.mutableCopy);

} else if ([value isKindOfClass:[NSAttributedString class]]) {

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,

meta->_setter,

(meta->_nsType == YYEncodingTypeNSString) ?

((NSAttributedString *)value).string :

((NSAttributedString *)value).string.mutableCopy);

}

} break;

case YYEncodingTypeNSValue:

case YYEncodingTypeNSNumber:

case YYEncodingTypeNSDecimalNumber: {

if (meta->_nsType == YYEncodingTypeNSNumber) {

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, YYNSNumberCreateFromID(value));

} else if (meta->_nsType == YYEncodingTypeNSDecimalNumber) {

if ([value isKindOfClass:[NSDecimalNumber class]]) {

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);

} else if ([value isKindOfClass:[NSNumber class]]) {

NSDecimalNumber *decNum = [NSDecimalNumber decimalNumberWithDecimal:[((NSNumber *)value) decimalValue]];

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, decNum);

} else if ([value isKindOfClass:[NSString class]]) {

NSDecimalNumber *decNum = [NSDecimalNumber decimalNumberWithString:value];

NSDecimal dec = decNum.decimalValue;

if (dec._length == 0 && dec._isNegative) {

decNum = nil; // NaN

}

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, decNum);

}

} else { // YYEncodingTypeNSValue

if ([value isKindOfClass:[NSValue class]]) {

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);

}

}

} break;

case YYEncodingTypeNSData:

case YYEncodingTypeNSMutableData: {

if ([value isKindOfClass:[NSData class]]) {

if (meta->_nsType == YYEncodingTypeNSData) {

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);

} else {

NSMutableData *data = ((NSData *)value).mutableCopy;

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, data);

}

} else if ([value isKindOfClass:[NSString class]]) {

NSData *data = [(NSString *)value dataUsingEncoding:NSUTF8StringEncoding];

if (meta->_nsType == YYEncodingTypeNSMutableData) {

data = ((NSData *)data).mutableCopy;

}

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, data);

}

} break;

case YYEncodingTypeNSDate: {

if ([value isKindOfClass:[NSDate class]]) {

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);

} else if ([value isKindOfClass:[NSString class]]) {

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, YYNSDateFromString(value));

}

} break;

case YYEncodingTypeNSURL: {

if ([value isKindOfClass:[NSURL class]]) {

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);

} else if ([value isKindOfClass:[NSString class]]) {

NSCharacterSet *set = [NSCharacterSet whitespaceAndNewlineCharacterSet];

NSString *str = [value stringByTrimmingCharactersInSet:set];

if (str.length == 0) {

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, nil);

} else {

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, [[NSURL alloc] initWithString:str]);

}

}

} break;

case YYEncodingTypeNSArray:

case YYEncodingTypeNSMutableArray: {

if (meta->_genericCls) {

NSArray *valueArr = nil;

if ([value isKindOfClass:[NSArray class]]) valueArr = value;

else if ([value isKindOfClass:[NSSet class]]) valueArr = ((NSSet *)value).allObjects;

if (valueArr) {

NSMutableArray *objectArr = [NSMutableArray new];

for (id one in valueArr) {

if ([one isKindOfClass:meta->_genericCls]) {

[objectArr addObject:one];

} else if ([one isKindOfClass:[NSDictionary class]]) {

Class cls = meta->_genericCls;

if (meta->_hasCustomClassFromDictionary) {

cls = [cls modelCustomClassForDictionary:one];

if (!cls) cls = meta->_genericCls; // for xcode code coverage

}

NSObject *newOne = [cls new];

[newOne modelSetWithDictionary:one];

if (newOne) [objectArr addObject:newOne];

}

}

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, objectArr);

}

} else {

if ([value isKindOfClass:[NSArray class]]) {

if (meta->_nsType == YYEncodingTypeNSArray) {

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);

} else {

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,

meta->_setter,

((NSArray *)value).mutableCopy);

}

} else if ([value isKindOfClass:[NSSet class]]) {

if (meta->_nsType == YYEncodingTypeNSArray) {

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, ((NSSet *)value).allObjects);

} else {

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,

meta->_setter,

((NSSet *)value).allObjects.mutableCopy);

}

}

}

} break;

case YYEncodingTypeNSDictionary:

case YYEncodingTypeNSMutableDictionary: {

if ([value isKindOfClass:[NSDictionary class]]) {

if (meta->_genericCls) {

NSMutableDictionary *dic = [NSMutableDictionary new];

[((NSDictionary *)value) enumerateKeysAndObjectsUsingBlock:^(NSString *oneKey, id oneValue, BOOL *stop) {

if ([oneValue isKindOfClass:[NSDictionary class]]) {

Class cls = meta->_genericCls;

if (meta->_hasCustomClassFromDictionary) {

cls = [cls modelCustomClassForDictionary:oneValue];

if (!cls) cls = meta->_genericCls; // for xcode code coverage

}

NSObject *newOne = [cls new];

[newOne modelSetWithDictionary:(id)oneValue];

if (newOne) dic[oneKey] = newOne;

}

}];

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, dic);

} else {

if (meta->_nsType == YYEncodingTypeNSDictionary) {

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);

} else {

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,

meta->_setter,

((NSDictionary *)value).mutableCopy);

}

}

}

} break;

case YYEncodingTypeNSSet:

case YYEncodingTypeNSMutableSet: {

NSSet *valueSet = nil;

if ([value isKindOfClass:[NSArray class]]) valueSet = [NSMutableSet setWithArray:value];

else if ([value isKindOfClass:[NSSet class]]) valueSet = ((NSSet *)value);

if (meta->_genericCls) {

NSMutableSet *set = [NSMutableSet new];

for (id one in valueSet) {

if ([one isKindOfClass:meta->_genericCls]) {

[set addObject:one];

} else if ([one isKindOfClass:[NSDictionary class]]) {

Class cls = meta->_genericCls;

if (meta->_hasCustomClassFromDictionary) {

cls = [cls modelCustomClassForDictionary:one];

if (!cls) cls = meta->_genericCls; // for xcode code coverage

}

NSObject *newOne = [cls new];

[newOne modelSetWithDictionary:one];

if (newOne) [set addObject:newOne];

}

}

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, set);

} else {

if (meta->_nsType == YYEncodingTypeNSSet) {

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, valueSet);

} else {

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,

meta->_setter,

((NSSet *)valueSet).mutableCopy);

}

}

} // break; commented for code coverage in next line

default: break;

}

}

} else {

BOOL isNull = (value == (id)kCFNull);

switch (meta->_type & YYEncodingTypeMask) {

case YYEncodingTypeObject: {

Class cls = meta->_genericCls ?: meta->_cls;

if (isNull) {

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)nil);

} else if ([value isKindOfClass:cls] || !cls) {

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)value);

} else if ([value isKindOfClass:[NSDictionary class]]) {

NSObject *one = nil;

if (meta->_getter) {

one = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter);

}

if (one) {

[one modelSetWithDictionary:value];

} else {

if (meta->_hasCustomClassFromDictionary) {

cls = [cls modelCustomClassForDictionary:value] ?: cls;

}

one = [cls new];

[one modelSetWithDictionary:value];

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)one);

}

}

} break;

case YYEncodingTypeClass: {

if (isNull) {

((void (*)(id, SEL, Class))(void *) objc_msgSend)((id)model, meta->_setter, (Class)NULL);

} else {

Class cls = nil;

if ([value isKindOfClass:[NSString class]]) {

cls = NSClassFromString(value);

if (cls) {

((void (*)(id, SEL, Class))(void *) objc_msgSend)((id)model, meta->_setter, (Class)cls);

}

} else {

cls = object_getClass(value);

if (cls) {

if (class_isMetaClass(cls)) {

((void (*)(id, SEL, Class))(void *) objc_msgSend)((id)model, meta->_setter, (Class)value);

}

}

}

}

} break;

case? YYEncodingTypeSEL: {

if (isNull) {

((void (*)(id, SEL, SEL))(void *) objc_msgSend)((id)model, meta->_setter, (SEL)NULL);

} else if ([value isKindOfClass:[NSString class]]) {

SEL sel = NSSelectorFromString(value);

if (sel) ((void (*)(id, SEL, SEL))(void *) objc_msgSend)((id)model, meta->_setter, (SEL)sel);

}

} break;

case YYEncodingTypeBlock: {

if (isNull) {

((void (*)(id, SEL, void (^)()))(void *) objc_msgSend)((id)model, meta->_setter, (void (^)())NULL);

} else if ([value isKindOfClass:YYNSBlockClass()]) {

((void (*)(id, SEL, void (^)()))(void *) objc_msgSend)((id)model, meta->_setter, (void (^)())value);

}

} break;

case YYEncodingTypeStruct:

case YYEncodingTypeUnion:

case YYEncodingTypeCArray: {

if ([value isKindOfClass:[NSValue class]]) {

const char *valueType = ((NSValue *)value).objCType;

const char *metaType = meta->_info.typeEncoding.UTF8String;

if (valueType && metaType && strcmp(valueType, metaType) == 0) {

[model setValue:value forKey:meta->_name];

}

}

} break;

case YYEncodingTypePointer:

case YYEncodingTypeCString: {

if (isNull) {

((void (*)(id, SEL, void *))(void *) objc_msgSend)((id)model, meta->_setter, (void *)NULL);

} else if ([value isKindOfClass:[NSValue class]]) {

NSValue *nsValue = value;

if (nsValue.objCType && strcmp(nsValue.objCType, "^v") == 0) {

((void (*)(id, SEL, void *))(void *) objc_msgSend)((id)model, meta->_setter, nsValue.pointerValue);

}

}

} // break; commented for code coverage in next line

default: break;

}

}

}

三百行的代碼

一點點看

這個類三種情況。

第一種情況是meta->_isCNumber =YES

第二種情況是meta->_nsType 有值

再就是其他



第一種情況meta->_isCNumber =YES

調用static force_inline NSNumber *YYNSNumberCreateFromID(__unsafe_unretained id value)

static force_inline NSNumber *YYNSNumberCreateFromID(__unsafe_unretained id value) { static NSCharacterSet *dot; static NSDictionary *dic; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ dot = [NSCharacterSet characterSetWithRange:NSMakeRange('.', 1)]; dic = @{@"TRUE" : @(YES), @"True" : @(YES), @"true" : @(YES), @"FALSE" : @(NO), @"False" : @(NO), @"false" : @(NO), @"YES" : @(YES), @"Yes" : @(YES), @"yes" : @(YES), @"NO" : @(NO), @"No" : @(NO), @"no" : @(NO), @"NIL" : (id)kCFNull, @"Nil" : (id)kCFNull, @"nil" : (id)kCFNull, @"NULL" : (id)kCFNull, @"Null" : (id)kCFNull, @"null" : (id)kCFNull, @"(NULL)" : (id)kCFNull, @"(Null)" : (id)kCFNull, @"(null)" : (id)kCFNull, @"" : (id)kCFNull, @"" : (id)kCFNull, @"" : (id)kCFNull};

});

if (!value || value == (id)kCFNull) return nil;

if ([value isKindOfClass:[NSNumber class]]) return value;

if ([value isKindOfClass:[NSString class]]) {

NSNumber *num = dic[value];

if (num != nil) {

if (num == (id)kCFNull) return nil;

return num;

}

if ([(NSString *)value rangeOfCharacterFromSet:dot].location != NSNotFound) {

const char *cstring = ((NSString *)value).UTF8String;

if (!cstring) return nil;

double num = atof(cstring);

if (isnan(num) || isinf(num)) return nil;

return @(num);

} else {

const char *cstring = ((NSString *)value).UTF8String;

if (!cstring) return nil;

return @(atoll(cstring));

}

}

return nil;

}

這個類簡單,就是講value 轉換成NSNumber

不做介紹

看下面的這個函數 這里面包含怎么將value 賦值到模型上的

static force_inline void ModelSetNumberToProperty(__unsafe_unretained id model,

__unsafe_unretained NSNumber *num,

__unsafe_unretained _YYModelPropertyMeta *meta) {

switch (meta->_type & YYEncodingTypeMask) {

case YYEncodingTypeBool: {

((void (*)(id, SEL, bool))(void *) objc_msgSend)((id)model, meta->_setter, num.boolValue);

} break;

case YYEncodingTypeInt8: {

((void (*)(id, SEL, int8_t))(void *) objc_msgSend)((id)model, meta->_setter, (int8_t)num.charValue);

} break;

case YYEncodingTypeUInt8: {

((void (*)(id, SEL, uint8_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint8_t)num.unsignedCharValue);

} break;

case YYEncodingTypeInt16: {

((void (*)(id, SEL, int16_t))(void *) objc_msgSend)((id)model, meta->_setter, (int16_t)num.shortValue);

} break;

case YYEncodingTypeUInt16: {

((void (*)(id, SEL, uint16_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint16_t)num.unsignedShortValue);

} break;

case YYEncodingTypeInt32: {

((void (*)(id, SEL, int32_t))(void *) objc_msgSend)((id)model, meta->_setter, (int32_t)num.intValue);

}

case YYEncodingTypeUInt32: {

((void (*)(id, SEL, uint32_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint32_t)num.unsignedIntValue);

} break;

case YYEncodingTypeInt64: {

if ([num isKindOfClass:[NSDecimalNumber class]]) {

((void (*)(id, SEL, int64_t))(void *) objc_msgSend)((id)model, meta->_setter, (int64_t)num.stringValue.longLongValue);

} else {

((void (*)(id, SEL, uint64_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint64_t)num.longLongValue);

}

} break;

case YYEncodingTypeUInt64: {

if ([num isKindOfClass:[NSDecimalNumber class]]) {

((void (*)(id, SEL, int64_t))(void *) objc_msgSend)((id)model, meta->_setter, (int64_t)num.stringValue.longLongValue);

} else {

((void (*)(id, SEL, uint64_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint64_t)num.unsignedLongLongValue);

}

} break;

case YYEncodingTypeFloat: {

float f = num.floatValue;

if (isnan(f) || isinf(f)) f = 0;

((void (*)(id, SEL, float))(void *) objc_msgSend)((id)model, meta->_setter, f);

} break;

case YYEncodingTypeDouble: {

double d = num.doubleValue;

if (isnan(d) || isinf(d)) d = 0;

((void (*)(id, SEL, double))(void *) objc_msgSend)((id)model, meta->_setter, d);

} break;

case YYEncodingTypeLongDouble: {

long double d = num.doubleValue;

if (isnan(d) || isinf(d)) d = 0;

((void (*)(id, SEL, long double))(void *) objc_msgSend)((id)model, meta->_setter, (long double)d);

} // break; commented for code coverage in next line

default: break;

}

}

賦值好簡單,采用objc_msgSend 方式進行賦值,原來數據就是這樣和model關聯起來的



第二種情況?meta->_nsType 不為0

1 當meta->_nsType == 0 ?給model 屬性值設置nil

2當meta->_nsType==YYEncodingTypeNSString 或者YYEncodingTypeNSMutableString

這里檢查value值是字符串,屬性要是

字符串直接給model 調用setting方法賦值,?

可變字符串,將value轉換成mutableCopy 字符串。

再次檢查 value值是數字,屬性要是

字符串,直接賦值,

可變字符串,將value轉換成mutalbleString

檢查value是NSData ,屬性要是

直接轉換成NSMutableString

檢查value 是NSURL ,屬性要是

要是字符串,直接將value 轉換成字符串

要是可變字符串,直接將value 轉換成可變字符串

檢查value 是NSAttributedString, 屬性要是

要是字符串,直接將value 轉換成字符串

要是可變字符串,直接將value 轉換成可變字符串

從這里可以看出來,NSString 可以接收NSNumber,NSUrl,NSData,NSAttributedString,NSString ,NSMutableString 類型的值

3.當meta->_nsType==YYEncodingTypeNSValue,YYEncodingTypeNSNumber,YYEncodingTypeNSDecimalNumber

當meta->_nsType==YYEncodingTypeNSNumber?

直接給model 賦值

當meta->_nsType == YYEncodingTypeNSDecimalNumber。這里要檢查value的值的類型

可以是NSDecimalNumber,NSNumber,NSString

當meta->_nsType ==YYEncodingTypeNSValue

這里只支持value 是NSValue 類型的值

4 當meta->_nsType==YYEncodingTypeNSData,YYEncodingTypeNSMutableData。檢查value ,可以是NSData 或者NSMutableData NSString

5當meta->_nsType==YYEncodingTypeNSDate ,value 值可以是NSDate 或者NSString

6當meta->_nsType==YYEncodingTypeNSURL value 值可以是NSURL 或者NSString,這里有刪除空白的功能

7.當meta->_nsType ==YYEncodingTypeNSMutableArray,YYEncodingTypeNSArray?

這里value 值可以是NSArray 或者是NSSet

NSMutableArray *objectArr = [NSMutableArray new];

for (id one in valueArr) {

if ([one isKindOfClass:meta->_genericCls]) {

[objectArr addObject:one];

} else if ([one isKindOfClass:[NSDictionary class]]) {

Class cls = meta->_genericCls;

if (meta->_hasCustomClassFromDictionary) {

cls = [cls modelCustomClassForDictionary:one];

if (!cls) cls = meta->_genericCls; // for xcode code coverage

}

NSObject *newOne = [cls new];

[newOne modelSetWithDictionary:one];

if (newOne) [objectArr addObject:newOne];

}

}

((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, objectArr);

這里判斷數組中對象是不是_genericCls 對象,是的話就直接將對象放入objectArr 數組中

判斷數組中對象是不是NSDictionary ,是的話,就同解析字典一樣,方式解析該類,將最后的解析結果放入objectArr 中,給model 發送setting方法,出入值objectArr

要是沒有NSArray 對應的模型

我們就直接將value這個數組原裝的的給model。不做處理

8 當meta->_nsType ==YYEncodingTypeNSDictionary ,YYEncodingTypeNSMutableDictionary的時候,這里只支持value 是NSDictionary

當有_genericCls 的時候,取出value 中的所有字典的值,要是其中的值有NSDictionary 的,就對值進行轉換

沒有_genericCls 就根據meta->_nsType類型直接賦值就好

9當meta->_nsType==YYEncodingTypeNSSet,YYEncodingTypeNSMutableSet

處理方式同數組相同



第三種情況?

就是一些特殊情況的處理

1.meta->_type ==YYEncodingTypeObject

當解析屬性是id類型的時候,獲取class,當value時nil 就給model 相關屬性賦值nil

當value值 是 cls 類時候,直接復制就行了,沒有cls 也直接賦值

當value 值是NSDictionary 就將其解析成模型,發送

其他的情況不處理,廢棄掉

2?meta->_type ==YYEncodingTypeClass

value 是nil?就給model 相關屬性賦值nil

value 值是NSString 就將NSString 轉換成class ,給model相關屬性賦值

其他的value 嘗試獲取下class,要是class 是元類的的話,就發送value。

3?meta->_type ==YYEncodingTypeSEL

value 是nil就給model 相關屬性賦值nil

value 是字符串,就將字符串轉換成SEL 給model 屬性賦值

4meta->_type ==YYEncodingTypeBlock

value是nil,給model 發送一個NULL block

別的判斷是不是block

/// Get the 'NSBlock' class.

static force_inline Class YYNSBlockClass() {

static Class cls;

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

void (^block)(void) = ^{};

cls = ((NSObject *)block).class;

while (class_getSuperclass(cls) != [NSObject class]) {

cls = class_getSuperclass(cls);

}

});

return cls; // current is "NSBlock"

}


獲取block的根block ?是NSBlock ,檢查value 是NSBlock 給model屬性賦值value

5meta->_type ==YYEncodingTypeStruct,YYEncodingTypeUnion,YYEncodingTypeCArray

這幾種類型,value 只能是NSValue 類型的

6meta->_type==YYEncodingTypePointer,YYEncodingTypeCString

只能接受nsvalue 類型的值,并且特定類型的。

這里就所有json轉model解析完畢了。

我們這里詳細匯總下

1 . ?設計到的類

YYClassInfo

YYClassPropertyInfo

YYClassMethodInfo

YYClassIvarInfo

_YYModelPropertyMeta

_YYModelMeta

NSObject+YYModel

NSArray+YYModel

NSDictionary+YYModel

2 每個類的屬性含義

YYClassInfo ?

@property (nonatomic, assign, readonly) Class cls; ///< class object

類 傳入的參數

@property (nullable, nonatomic, assign, readonly) Class superCls; ///< super class object

自己的父類

@property (nullable, nonatomic, assign, readonly) Class metaCls; ///< class's meta class object

元類

@property (nonatomic, readonly) BOOL isMeta; ///< whether this class is meta class

自己是否是元類

@property (nonatomic, strong, readonly) NSString *name; ///< class name

類的名字

@property (nullable, nonatomic, strong, readonly) YYClassInfo *superClassInfo; ///< super class's class info

父類的解析類

@property (nullable, nonatomic, strong, readonly) NSDictionary*ivarInfos; ///< ivars

類所有的變量字典不包含父類的變量,key是字符串 ,變量的name, value是YYClassIvarInfo

@property (nullable, nonatomic, strong, readonly) NSDictionary*methodInfos; ///< methods

該類的方法,不包含父類的方法 ,key是字符串-方法的name ,value是YYClassMethodInfo

@property (nullable, nonatomic, strong, readonly) NSDictionary*propertyInfos; ///< properties

該類的屬性,不包含父類的方法,key是字符串-屬性的name ,value是YYClassPropertyInfo

YYClassPropertyInfo

@property (nonatomic, assign, readonly) objc_property_t property; ///< property's opaque struct

代表屬性,

@property (nonatomic, strong, readonly) NSString *name; ///< property's name

屬性的名字

@property (nonatomic, assign, readonly) YYEncodingType type; ///< property's type

屬性編碼

@property (nonatomic, strong, readonly) NSString *typeEncoding; ///< property's encoding value

屬性修飾類的編碼 eg(該類的NSString 編碼是 NSString * )

@property (nonatomic, strong, readonly) NSString *ivarName; ///< property's ivar name

變量名字

@property (nullable, nonatomic, assign, readonly) Class cls; ///< may be nil

屬性的修飾的類名 ? ?eg:(該條屬性的 Class)

@property (nullable, nonatomic, strong, readonly) NSArray*protocols; ///< may nil

屬性的協議

@property (nonatomic, assign, readonly) SEL getter;? ? ? ? ? ? ? ///< getter (nonnull)

getter方法

@property (nonatomic, assign, readonly) SEL setter;? ? ? ? ? ? ? ///< setter (nonnull)

setting 方法

YYClassMethodInfo

@property (nonatomic, assign, readonly) Method method; ///< method opaque struct

代表類的一個方法

@property (nonatomic, strong, readonly) NSString *name; ///< method name

方法 的名字

@property (nonatomic, assign, readonly) SEL sel; ///< method's selector

方法的SEL

@property (nonatomic, assign, readonly) IMP imp; ///< method's implementation

方法的IMP

@property (nonatomic, strong, readonly) NSString *typeEncoding; ///< method's parameter and return types

方法的編碼

@property (nonatomic, strong, readonly) NSString *returnTypeEncoding; ///< return value's type

返回值類型

@property (nullable, nonatomic, strong, readonly) NSArray*argumentTypeEncodings; ///< array of arguments' type

參數類型編碼數組

YYClassIvarInfo

@property (nonatomic, assign, readonly) Ivar ivar; ///< ivar opaque struct

代表一個變量

@property (nonatomic, strong, readonly) NSString *name;? ? ? ? ///< Ivar's name

變量名字

@property (nonatomic, assign, readonly) ptrdiff_t offset;? ? ? ///< Ivar's offset

變量的偏移

@property (nonatomic, strong, readonly) NSString *typeEncoding; ///< Ivar's type encoding

變量的編碼 字符串類型

@property (nonatomic, assign, readonly) YYEncodingType type;? ? ///< Ivar's type

變量的編碼 枚舉類型

以上四個類其實就是將Class 給解析成是個部分而已

_YYModelPropertyMeta ?


NSString *_name; ///< property's name

屬性的名字

YYEncodingType _type;? ? ? ? ///< property's type

屬性的類型

YYEncodingNSType _nsType;? ? ///< property's Foundation type

屬性foundataion 類型,

BOOL _isCNumber;? ? ? ? ? ? ///< is c number type

是不是數字

Class _cls;? ? ? ? ? ? ? ? ? ///< property's class, or nil

屬性修飾的名字

Class _genericCls;? ? ? ? ? ///< container's generic class, or nil if threr's no generic class

代表屬性需要轉換成的類

SEL _getter;? ? ? ? ? ? ? ? ///< getter, or nil if the instances cannot respond

屬性的get方法

SEL _setter;? ? ? ? ? ? ? ? ///< setter, or nil if the instances cannot respond

屬性的set方法

BOOL _isKVCCompatible;? ? ? ///< YES if it can access with key-value coding

BOOL _isStructAvailableForKeyedArchiver; ///< YES if the struct can encoded with keyed archiver/unarchiver

特定的NS 結構體CGSize

BOOL _hasCustomClassFromDictionary; ///< class/generic class implements +modelCustomClassForDictionary:

代表 屬性修飾的類 是否實現了modelCustomClassForDictionary: 方法

/*

property->key:? ? ? _mappedToKey:key? ? _mappedToKeyPath:nil? ? ? ? ? ? _mappedToKeyArray:nil

property->keyPath:? _mappedToKey:keyPath _mappedToKeyPath:keyPath(array) _mappedToKeyArray:nil

property->keys:? ? ? _mappedToKey:keys[0] _mappedToKeyPath:nil/keyPath? ? _mappedToKeyArray:keys(array)

*/

NSString *_mappedToKey;? ? ? ///< the key mapped to

匹配json 中key 的名字 。?

NSArray *_mappedToKeyPath;? ///< the key path mapped to (nil if the name is not key path)


NSArray *_mappedToKeyArray;? ///< the key(NSString) or keyPath(NSArray) array (nil if not mapped to multiple keys)


YYClassPropertyInfo *_info;? ///< property's info

代表一條屬性

_YYModelPropertyMeta *_next; ///< next meta if there are multiple properties mapped to the same key.

因為模型中的屬性 更改匹配json中的特定的key。可能導致json中key匹配model中多個屬性,所以用_next標記所有的屬性匹配到的相同的key。?



_YYModelMeta

這個類很關鍵,類似個manger ,將所有的數據組裝起來的

YYClassInfo *_classInfo;

代表一個類,

/// Key:mapped key and key path, Value:_YYModelPropertyMeta.

NSDictionary *_mapper;

key 是json 中的key值,value 是?_YYModelPropertyMeta?

/// Array<_YYModelPropertyMeta>, all property meta of this model.

NSArray *_allPropertyMetas;

代表該類包括父類的所有的屬性。

/// Array<_YYModelPropertyMeta>, property meta which is mapped to a key path.

NSArray *_keyPathPropertyMetas;


/// Array<_YYModelPropertyMeta>, property meta which is mapped to multi keys.

NSArray *_multiKeysPropertyMetas;


/// The number of mapped key (and key path), same to _mapper.count.

NSUInteger _keyMappedCount;

屬性的數量

/// Model class type.

YYEncodingNSType _nsType;

ns類型

BOOL _hasCustomWillTransformFromDictionary;

是否含有modelCustomWillTransformFromDictionary 函數

BOOL _hasCustomTransformFromDictionary;

是否含有modelCustomTransformFromDictionary 函數

BOOL _hasCustomTransformToDictionary;

是否含有modelCustomTransformToDictionary函數

BOOL _hasCustomClassFromDictionary;

是否含有modelCustomClassForDictionary 函數


3 類屬性之間的關聯關系


4 解析思路json 轉 model

《1》 解析model ?,解析成YYClassInfo。

《2》解析YYClassInfo,把self 和superClassInfo 中的每一條屬性,名字作為key ,屬性具體解析成_YYModelPropertyMeta作為值,存放到_YYModelMeta 的_mapper 值中。

《3》遍歷字典 數據,獲取字典的key 和value,檢查_mapper 中是否還有key ,有就從_mapper 中獲取到_YYModelPropertyMeta (代表setting方法)。model 通過_YYModelPropertyMeta(setting方法) 設置value。

《4》這里value是字典,就重復《1》的步驟

《5》這里value是數組,就遍歷value ,每一項重復《1》的步驟


5 要是類實現了方法?modelCustomPropertyMapper,那么設計到的屬性有_YYModelPropertyMeta類中的_mappedToKey,_mappedToKeyPath,_next,_mappedToKeyArray,_keyPathPropertyMetas,_multiKeysPropertyMetas

每個屬性的具體解析

_mappedToKey

該屬性匹配 json中的key(屬性名字和json中 key可以不一樣),正常是屬性的名字和json中的key一樣。

例如

+ (NSDictionary *)modelCustomPropertyMapper {return @{@"statusID" : @"id"}

結果是

_mappedToKey = @"id"

_mappedToKeyPath

要是值是點屬性。該屬性記錄的是以點分割的字符串的數組。

例如

+ (NSDictionary *)modelCustomPropertyMapper {

return @{@"statusID" : @"id.name"}

結果是

_mappedToKeyPath=@[@"id",@"name"]

_mappedToKeyArray

要是值是數組,該屬性記錄的是點分割的字符串的數組

例如

+ (NSDictionary *)modelCustomPropertyMapper {return @{@"statusID" : @[@"id.name"]}

結果是

_mappedToKeyArray=@[@[@"id",@"name"]]

例如+ (NSDictionary *)modelCustomPropertyMapper {return @{@"statusID" : @[@"id"]}

結果是_mappedToKeyArray=@[@"id"]


_keyPathPropertyMetas

包含_mappedToKeyPath不是空 的屬性的數組

_next ?

記錄的是json中的key,需要解析到model中的屬性。

_multiKeysPropertyMetas

包含value 是數組 的屬性的數組

當json 轉換model 調用函數- (BOOL)yy_modelSetWithDictionary:(NSDictionary *)dic

if (modelMeta->_keyPathPropertyMetas) {

? ? ? ? ? ? CFArrayApplyFunction((CFArrayRef)modelMeta->_keyPathPropertyMetas,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_keyPathPropertyMetas)),

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ModelSetWithPropertyMetaArrayFunction,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? &context);

? ? ? ? }

調用函數

static void ModelSetWithPropertyMetaArrayFunction(const void *_propertyMeta, void *_context) {

? ? ModelSetContext *context = _context;

? ? __unsafe_unretained NSDictionary *dictionary = (__bridge NSDictionary *)(context->dictionary);

? ? __unsafe_unretained _YYModelPropertyMeta *propertyMeta = (__bridge _YYModelPropertyMeta *)(_propertyMeta);

? ? if (!propertyMeta->_setter) return;

? ? id value = nil;


? ? if (propertyMeta->_mappedToKeyArray) {

? ? ? ? value = YYValueForMultiKeys(dictionary, propertyMeta->_mappedToKeyArray);

? ? } else if (propertyMeta->_mappedToKeyPath) {

? ? ? ? value = YYValueForKeyPath(dictionary, propertyMeta->_mappedToKeyPath);

? ? } else {

? ? ? ? value = [dictionary objectForKey:propertyMeta->_mappedToKey];

? ? }


? ? if (value) {

? ? ? ? __unsafe_unretained id model = (__bridge id)(context->model);

? ? ? ? ModelSetValueForProperty(model, value, propertyMeta);

? ? }

}

我們知道,當_keyPathPropertyMetas 不為nil 的時候,對應的是_mappedToKeyPath ,調用下面的函數

static force_inline id YYValueForKeyPath(__unsafe_unretained NSDictionary *dic, __unsafe_unretained NSArray *keyPaths) {

? ? id value = nil;

? ? for (NSUInteger i = 0, max = keyPaths.count; i < max; i++) {

? ? ? ? value = dic[keyPaths[i]];

? ? ? ? if (i + 1 < max) {

? ? ? ? ? ? if ([value isKindOfClass:[NSDictionary class]]) {

? ? ? ? ? ? ? ? dic = value;

? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? return nil;

? ? ? ? ? ? }

? ? ? ? }

? ? }

? ? return value;

}

這個函數很簡單,按照keyPaths 順序依次查找,要是查找到字典,那么就從新字典里面查找下面的key,其實就是按照kvc path 查找到最后的值。有就匹配,沒有就返回。

這里我們知道_mappedToKeyPath 盛放的數據是 kvc path ,我們把path給用點分割成順序數組。查找與_YYModelPropertyMeta 匹配的字典。

舉個例子

@interface YYTestModel : NSObject

@property (nonatomic ,strong) YYDelegateModel * model;

@property (nonatomic ,strong)NSString * name;

@property (nonatomic ,strong) NSString * userID;

@end

@implementation YYTestModel

+ (NSDictionary *)modelCustomPropertyMapper {

? ? return @{ @"model" : @"user.badge",

? ? ? ? ? ? ? };

}

@end

@interface YYDelegateModel : NSObject

@property (nonatomic ,strong) NSString *zongyiji;

@end

@implementation YYDelegateModel

@end


json數據

{

"user" : {

"badge" : {

"zongyiji" : 1,

}

}

}

調用

YYTestModel * md=[YYTestModel modelWithDictionary:json];

我們這里@"model" : @"user.badge" ?我們這里知道YYTestModel 的_mappedToKeyPath 值是@[@user,@badge] 調用YYValueForKeyPath 方法,

獲取到的值是value值是 ?"zongyiji" : 1, ??

_mappedToKeyArray 這個屬性關聯的函數是

static force_inline id YYValueForMultiKeys(__unsafe_unretained NSDictionary *dic, __unsafe_unretained NSArray *multiKeys) {

? ? id value = nil;

? ? for (NSString *key in multiKeys) {

? ? ? ? if ([key isKindOfClass:[NSString class]]) {

? ? ? ? ? ? value = dic[key];

? ? ? ? ? ? if (value) break;

? ? ? ? } else {

? ? ? ? ? ? value = YYValueForKeyPath(dic, (NSArray *)key);

? ? ? ? ? ? if (value) break;

? ? ? ? }

? ? }

? ? return value;

}

我們知道_mappedToKeyArray 可能裝有字符串, 也可能是數組

1 字符串簡單,直接從dic 中獲取到

2要是數組呢, 假設?_mappedToKeyArray 值是@[@[@"id",@"name"],@[@"ss",@"dd"]]

那么先取出@["id",@"name"] 調用YYValueForKeyPath 查詢改kvc 路徑是否包含,要是有的話就返回了。沒有就查找第二條路徑@[@"ss",@"dd"],沒有就結束,有就返回第二條路徑的值。

這里算是把json 轉換成 model完全分析完畢了。



接下來分析model 轉 json

- (id)modelToJSONObject {

? ? /*

? ? Apple said:

? ? The top level object is an NSArray or NSDictionary.

? ? All objects are instances of NSString, NSNumber, NSArray, NSDictionary, or NSNull.

? ? All dictionary keys are instances of NSString.

? ? Numbers are not NaN or infinity.

? ? */

? ? id jsonObject = ModelToJSONObjectRecursive(self);

? ? if ([jsonObject isKindOfClass:[NSArray class]]) return jsonObject;

? ? if ([jsonObject isKindOfClass:[NSDictionary class]]) return jsonObject;

? ? return nil;

}


這里關鍵是下面的這個c函數 static id ModelToJSONObjectRecursive(NSObject *model)

看看具體咋實現的

分段看

if (!model || model == (id)kCFNull) return model;

檢查model 是否合法。不合法就返回自己

if ([model isKindOfClass:[NSString class]]) return model;

檢查model 是字符串,是就返回自己

if ([model isKindOfClass:[NSNumber class]]) return model;

檢查model是NSNumber 就返回自己

if ([model isKindOfClass:[NSDictionary class]]) {

? ? ? ? if ([NSJSONSerialization isValidJSONObject:model]) return model;

? ? ? ? NSMutableDictionary *newDic = [NSMutableDictionary new];

? ? ? ? [((NSDictionary *)model) enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {

? ? ? ? ? ? NSString *stringKey = [key isKindOfClass:[NSString class]] ? key : key.description;

? ? ? ? ? ? if (!stringKey) return;

? ? ? ? ? ? id jsonObj = ModelToJSONObjectRecursive(obj);

? ? ? ? ? ? if (!jsonObj) jsonObj = (id)kCFNull;

? ? ? ? ? ? newDic[stringKey] = jsonObj;

? ? ? ? }];

? ? ? ? return newDic;

? ? }

檢查model是字典,檢查字典是否是有效的json,有效json 就直接返回model。不是有效json 那么,就枚舉字典,value 值重新調用static id ModelToJSONObjectRecursive(NSObject *model) 方法,把數據重新存放到新字典中。

根據官方文檔NSJSONSerialization 中對json 對象的解釋

A Foundation object that may be converted to JSON must have the following properties:

The top level object is an?NSArray?or?NSDictionary.

All objects are instances of?NSString,?NSNumber,?NSArray,?NSDictionary, or?NSNull.

All dictionary keys are instances of?NSString.

Numbers are not NaN or infinity.

Other rules may apply. Calling?isValidJSONObject:?or attempting a conversion are the definitive ways to tell if a given object can be converted to JSON data.

if ([model isKindOfClass:[NSSet class]]) {

? ? ? ? NSArray *array = ((NSSet *)model).allObjects;

? ? ? ? if ([NSJSONSerialization isValidJSONObject:array]) return array;

? ? ? ? NSMutableArray *newArray = [NSMutableArray new];

? ? ? ? for (id obj in array) {

? ? ? ? ? ? if ([obj isKindOfClass:[NSString class]] || [obj isKindOfClass:[NSNumber class]]) {

? ? ? ? ? ? ? ? [newArray addObject:obj];

? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? id jsonObj = ModelToJSONObjectRecursive(obj);

? ? ? ? ? ? ? ? if (jsonObj && jsonObj != (id)kCFNull) [newArray addObject:jsonObj];

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? return newArray;

? ? }

要是mode是NSSet ,那么僵set轉換成nsarray,檢查nsarray是否是有效的json 是json,返回array。不是json,那么獲取nsarray 中的每個數據,要是數據是NSString 或者是NSNumber ,就直接加入到新NSArray中,不是就遞歸static id ModelToJSONObjectRecursive(NSObject *model),獲取的數據加入到新NSArray 中,返回新數組

if ([model isKindOfClass:[NSArray class]]) {

? ? ? ? if ([NSJSONSerialization isValidJSONObject:model]) return model;

? ? ? ? NSMutableArray *newArray = [NSMutableArray new];

? ? ? ? for (id obj in (NSArray *)model) {

? ? ? ? ? ? if ([obj isKindOfClass:[NSString class]] || [obj isKindOfClass:[NSNumber class]]) {

? ? ? ? ? ? ? ? [newArray addObject:obj];

? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? id jsonObj = ModelToJSONObjectRecursive(obj);

? ? ? ? ? ? ? ? if (jsonObj && jsonObj != (id)kCFNull) [newArray addObject:jsonObj];

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? return newArray;

? ? }

要是model 是NSArray ,處理方式同NSSet ,不解釋

if ([model isKindOfClass:[NSURL class]]) return ((NSURL *)model).absoluteString;

要是model是url 那么久返回url的字符串

if ([model isKindOfClass:[NSAttributedString class]]) return ((NSAttributedString *)model).string;

要是model是?NSAttributedString,那么返回model的string

if ([model isKindOfClass:[NSDate class]]) return [YYISODateFormatter() stringFromDate:(id)model];

要是model 是nsdate ,那么就將nsdate 轉換成日期字符串,字符串格式是formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ";

static force_inline NSDateFormatter *YYISODateFormatter() {

? ? static NSDateFormatter *formatter = nil;

? ? static dispatch_once_t onceToken;

? ? dispatch_once(&onceToken, ^{

? ? ? ? formatter = [[NSDateFormatter alloc] init];

? ? ? ? formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];

? ? ? ? formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ";

? ? });

? ? return formatter;

}

if ([model isKindOfClass:[NSData class]]) return nil;

要是model 是nsdata ,那么就返回nil ,因為nsdata 無法不確定是否能轉換成NSString,或者nsnumber,其實這里可以嘗試轉換。

要是都不是以上的類型,那么就是nsobject 類型的

_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:[model class]];

? ? if (!modelMeta || modelMeta->_keyMappedCount == 0) return nil;

NSObject 類型,那么就先解析該模型,獲取到該類的?_YYModelMeta(相當于manager)

[modelMeta->_mapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyMappedKey, _YYModelPropertyMeta *propertyMeta, BOOL *stop){

。。。。。

}

枚舉該對象的所有屬性

if (!propertyMeta->_getter) return;

?? id value = nil;

檢查屬性的getter方法,沒有就返回

if (propertyMeta->_isCNumber) {

? ? ? ? ? ? value = ModelCreateNumberFromProperty(model, propertyMeta);

? ? ? }

檢查屬性是不是數字,是數字,就獲取model 通過_YYModelPropertyMeta 的getter方法獲取該屬性的值。這里調用了static force_inline NSNumber *ModelCreateNumberFromProperty(__unsafe_unretained id model,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? __unsafe_unretained _YYModelPropertyMeta *meta) 方法,這個方法很簡單,不做介紹了。

if (propertyMeta->_nsType) {

? ? ? ? ? ? id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);

? ? ? ? ? ? value = ModelToJSONObjectRecursive(v);

? ? ? ? }

該條屬性要是_nsType ,我們就獲取該條屬性getter方法的值。將獲取的value 進行調用static id ModelToJSONObjectRecursive(NSObject *model) 方法遞歸。

switch (propertyMeta->_type & YYEncodingTypeMask) {

? ? ? ? ? ? ? ? case YYEncodingTypeObject: {

? ? ? ? ? ? ? ? ? ? id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);

? ? ? ? ? ? ? ? ? ? value = ModelToJSONObjectRecursive(v);

? ? ? ? ? ? ? ? ? ? if (value == (id)kCFNull) value = nil;

? ? ? ? ? ? ? ? } break;

? ? ? ? ? ? ? ? case YYEncodingTypeClass: {

? ? ? ? ? ? ? ? ? ? Class v = ((Class (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);

? ? ? ? ? ? ? ? ? ? value = v ? NSStringFromClass(v) : nil;

? ? ? ? ? ? ? ? } break;

? ? ? ? ? ? ? ? case YYEncodingTypeSEL: {

? ? ? ? ? ? ? ? ? ? SEL v = ((SEL (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);

? ? ? ? ? ? ? ? ? ? value = v ? NSStringFromSelector(v) : nil;

? ? ? ? ? ? ? ? } break;

? ? ? ? ? ? ? ? default: break;

? ? ? ? ? ? }

檢查屬性的類型,要是YYEncodingTypeObject 類型,還是獲取該model 通過propertyMeta 的getter方法獲取的值,將值進行遞歸。

要是屬性是YYEncodingTypeClass ,那么就通過propertyMeta 的getter方法獲取class值。將class 轉換成字符串。

要是屬性是YYEncodingTypeSEL 方法,同理獲取sel 。將sel 處理成字符串返回

if (!value) return

要是沒有值,那么直接返回

if (propertyMeta->_mappedToKeyPath) {

? ? ? ? ? ? NSMutableDictionary *superDic = dic;

? ? ? ? ? ? NSMutableDictionary *subDic = nil;

? ? ? ? ? ? for (NSUInteger i = 0, max = propertyMeta->_mappedToKeyPath.count; i < max; i++) {

? ? ? ? ? ? ? ? NSString *key = propertyMeta->_mappedToKeyPath[i];

? ? ? ? ? ? ? ? if (i + 1 == max) { // end

? ? ? ? ? ? ? ? ? ? if (!superDic[key]) superDic[key] = value;

? ? ? ? ? ? ? ? ? ? break;

? ? ? ? ? ? ? ? }


? ? ? ? ? ? ? ? subDic = superDic[key];

? ? ? ? ? ? ? ? if (subDic) {

? ? ? ? ? ? ? ? ? ? if ([subDic isKindOfClass:[NSDictionary class]]) {

? ? ? ? ? ? ? ? ? ? ? ? subDic = subDic.mutableCopy;

? ? ? ? ? ? ? ? ? ? ? ? superDic[key] = subDic;

? ? ? ? ? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? ? ? ? ? break;

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? ? ? subDic = [NSMutableDictionary new];

? ? ? ? ? ? ? ? ? ? superDic[key] = subDic;

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? superDic = subDic;

? ? ? ? ? ? ? ? subDic = nil;

? ? ? ? ? ? }

? ? ? ? } else {

? ? ? ? ? ? if (!dic[propertyMeta->_mappedToKey]) {

? ? ? ? ? ? ? ? dic[propertyMeta->_mappedToKey] = value;

? ? ? ? ? ? }

? ? ? ? }

? ? }];

這里要是_mappedToKeyPath 屬性有值,說明是kvc 模式,eg "user.id"

轉換成格式肯定是

{

"user":{

id:"value"

}

}

下面就是進行這樣的轉換,

user 的value 是NSMutableDictionary

id 的value 是 真正的值

第一步獲取superDic 就是跟字典root

第二步 將superDic 設置user 對應的字典subDic

第三步,讓superDic 指向subDic 設置id 對應的值?


要是沒有_mappedToKeyPath 呢?那么就說明沒有kvc 格式匹配。字典設置的key 值是_mappedToKey

就是json 轉model的逆運算,簡單介紹。

static NSString *ModelDescription(NSObject *model)

這個方法也model 手動轉換成json ,原理一樣的。不做介紹了。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,570評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,505評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,017評論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,786評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,219評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,438評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,971評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,796評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,995評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,230評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,918評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,697評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容