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 NSStringNSNumberNSArrayNSDictionary, 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 ,原理一样的。不做介绍了。

推荐阅读更多精彩内容