YYModel 源码分析:模型转字典

2,817 阅读3分钟

YYModel , 模型字典,自动转化,

模型转字典, 主要是通过运行时,把模型的属性名取出来,递归构建字典

YYModel 设计 4 个模型,记录类与其属性信息,

  • 类 + 信息记录

  • 属性 + 信息记录

  • 属性

两个用于类,两个用于属性

如果从上到下,逐个包含,就很清晰

YYModel 为了方便逻辑处理,四个模型包含交叉,有点绕

本文通过模型转字典,看下其运转

调用:
    NSData* jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding]; 
    Book * page = [Book yy_modelWithJSON: jsonData];
    NSDictionary * dict = [page yy_modelToJSONObject];
实现:
- (id)yy_modelToJSONObject {
    // 取出 json
    id jsonObject = ModelToJSONObjectRecursive(self);
    // 判断数组
    if ([jsonObject isKindOfClass:[NSArray class]]) return jsonObject;
    // 判断字典
    if ([jsonObject isKindOfClass:[NSDictionary class]]) return jsonObject;
    return nil;
}

对模型,递归处理

 // 拿到模型,算出字典
static id ModelToJSONObjectRecursive(NSObject *model) {
// 不存在,就返回
    if (!model || model == (id)kCFNull) return model;
    // 抵达终端的值
    if ([model isKindOfClass:[NSString class]]) return model;
    if ([model isKindOfClass:[NSNumber class]]) return model;
    // ...
    // 处理字典
    // 处理集合 Set
    // 处理数组
    if ([model isKindOfClass:[NSURL class]]) return ((NSURL *)model).absoluteString;
    if ([model isKindOfClass:[NSAttributedString class]]) return ((NSAttributedString *)model).string;
    if ([model isKindOfClass:[NSDate class]]) return @"ha ha";
    if ([model isKindOfClass:[NSData class]]) return nil;
    
    
    _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:[model class]];
    if (!modelMeta) return nil;
    
    // 建立字典
    NSMutableDictionary *result = [[NSMutableDictionary alloc] init];
    __unsafe_unretained NSMutableDictionary *dic = result; // avoid retain and release in block
    [modelMeta->_mapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyMappedKey, _YYModelPropertyMeta *propertyMeta, BOOL *stop) {
        if (!propertyMeta->_getter) return;
        // 获取值
        id value = nil;
        if (propertyMeta->_isCNumber) {
            value = ModelCreateNumberFromProperty(model, propertyMeta);
        } else if (propertyMeta->_nsTypeX) {
            // _nsTypeX 是苹果默认对象
            id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
            value = ModelToJSONObjectRecursive(v);
        } else {
            switch (propertyMeta->_typeA & 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;
            }
        }
        if (!value) return;
        
        if (!dic[propertyMeta->_mappedToKey]) {
            // 赋值
            dic[propertyMeta->_mappedToKey] = value;
        }
    }];
    return result;
}

其中,

if (propertyMeta->_nsTypeX)

如果 _nsTypeX 不存在,就是 _nsTypeX = 0, YYEncodingTypeNSUnknown

结合下面代码,

如果 _nsTypeXNSStringNSNumberNSValueNSDataNSArrayNSDictionary, 就递归处理掉


static YYEncodingNSType YYClassGetNSType(Class cls){
    if (!cls) return YYEncodingTypeNSUnknown;
    if ([cls isSubclassOfClass:[NSString class]]) return YYEncodingTypeNSString;
    if ([cls isSubclassOfClass:[NSNumber class]]) return YYEncodingTypeNSNumber;
    if ([cls isSubclassOfClass:[NSValue class]]) return YYEncodingTypeNSValue;
    if ([cls isSubclassOfClass:[NSData class]]) return YYEncodingTypeNSData;
    if ([cls isSubclassOfClass:[NSArray class]]) return YYEncodingTypeNSArray;
    if ([cls isSubclassOfClass:[NSDictionary class]]) return YYEncodingTypeNSDictionary;
    return YYEncodingTypeNSUnknown;
}

runtime:

@interface NSObject (Model)

NSObject 加扩展,model 可以直接调用

  • - (BOOL)yy_modelSetWithDictionary:(NSDictionary *)dic 方法中,
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:object_getClass(self)];

@interface _YYModelMeta: NSObject

类信息,并添加自定义信息

先类方法,再实例方法

类方法加锁,做缓存

实例方法,处理属性

  • + (instancetype)metaWithClass:(Class)cls 方法中,
meta = [[_YYModelMeta alloc] initWithClass:cls];
  • - (instancetype)initWithClass:(Class)cls 方法中,
YYClassInfo *classInfo = [YYClassInfo classInfoWithClass:cls];

@interface YYClassInfo : NSObject

类信息

先类方法,再实例方法

类方法加锁,做缓存

实例方法,处理属性

  • + (instancetype)classInfoWithClass:(Class)cls 方法中,
info = [[YYClassInfo alloc] initWithClass:cls];
  • - (instancetype)initWithClass:(Class)cls 方法中,

- (instancetype)initWithClass:(Class)cls {
    // ...
    unsigned int propertyCount = 0;
    // 从类取出属性
    objc_property_t *properties = class_copyPropertyList(cls, &propertyCount);
    if (properties) {
        NSMutableDictionary *propertyInfos = [NSMutableDictionary new];
        _propertyInfos = propertyInfos;
        for (unsigned int i = 0; i < propertyCount; i++) {
            // 实例化,每一个属性
            YYClassPropertyInfo *info = [[YYClassPropertyInfo alloc] initWithProperty:properties[i]];
            if (info.name) propertyInfos[info.name] = info;
        }
        free(properties);
    }
    // ...
    return self;
}

@interface YYClassPropertyInfo : NSObject

属性信息

- (instancetype)initWithProperty:(objc_property_t)property {
    if (!property) return nil;
    self = [super init];
    const char *name = property_getName(property);
    if (name) {
         // 获取属性名,做了一个 UTF8 编码
        _name = [NSString stringWithUTF8String:name];
    }
    
    YYEncodingType type = 0;
    unsigned int attrCount;
    // 从属性中,翻出所有的信息
    objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount);
    for (unsigned int i = 0; i < attrCount; i++) {
        // 处理属性中,每一条信息
        switch (attrs[i].name[0]) {
            case 'T': { // Type encoding
                if (attrs[i].value) {
                    // 拿到编码方式
                    _typeEncoding = [NSString stringWithUTF8String:attrs[i].value];
                    type = YYEncodingGetType(attrs[i].value);
                    
                    if ((type & YYEncodingTypeMask) == YYEncodingTypeObject && _typeEncoding.length) {
                        NSScanner *scanner = [NSScanner scannerWithString:_typeEncoding];
                        if (![scanner scanString:@"@\"" intoString:NULL]) continue;
                        
                        NSString *clsName = nil;
                        if ([scanner scanUpToCharactersFromSet: [NSCharacterSet characterSetWithCharactersInString:@"\"<"] intoString:&clsName]) {
                            // 拿到类信息
                            if (clsName.length) _clsX = objc_getClass(clsName.UTF8String);
                        }
                        // _protocols
                        // ...
                    }
                }
            } break;
            // ...
        }
    }
    // ...
    return self;
}


@interface _YYModelPropertyMeta: NSObject

属性信息,和属性的添加类中


+ (instancetype)metaWithClassInfo:(YYClassInfo *)classInfo propertyInfo:(YYClassPropertyInfo *)propertyInfo generic:(Class)generic {
    // ...
    _YYModelPropertyMeta *meta = [self new];
    // 把上一步,获取的属性信息,拷贝出来
    meta->_name = propertyInfo.name;
    meta->_typeA = propertyInfo.type;
    meta->_info = propertyInfo;
    // ...
    
    
    // 获取类型信息
    if ((meta->_typeA & YYEncodingTypeMask) == YYEncodingTypeObject) {
        meta->_nsTypeX = YYClassGetNSType(propertyInfo.clsX);
    } else {
        meta->_isCNumber = YYEncodingTypeIsCNumber(meta->_typeA);
    }
模型转字典部分,通过记录的属性类型,处理

是数字,直接处理

非数字,系统类型优先处理

最后的情况下,处理对象等

相关 💦 文

YYModel 源码分析:模型设计

YYModel 源码分析:字典转模型

相关 💦 💧 repo

🌊 repo