YYModel源代碼分析(三)NSObject+YYModel

前言

本文的中文注釋代碼demo更新在我的github上相嵌。

上篇 YYModel源代碼分析(二)YYClassInfo
主要分析了YYClassInfo文件蹋盆。本篇會主要集中在NSObject+YYModel文件上。文章內容會包含一些與JSONModel的比較那先,想了解JSONModel,可以參考JSONModel源代碼解析赡艰。

主體分層

NSObject+YYModel主要分為以下幾個部分:

  • 內部使用的C函數(shù)部分
  • @interface _YYModelPropertyMeta : NSObject
  • @interface _YYModelMeta : NSObject
  • @interface NSObject (YYModel) : NSObject
  • @interface NSArray (YYModel) : NSObject
  • @interface NSDictionary (YYModel) : NSObject
  • @protocol YYModel <NSObject>:接口YYModel

由于代碼較多售淡,所以會挑重點的部分進行介紹。

NSObject+YYModel源代碼

@interface _YYModelPropertyMeta : NSObject

聲明

/// A property info in object model.
// model property的進一步分裝
@interface _YYModelPropertyMeta : NSObject {
    @package
    NSString *_name;             ///< property's name                   //property名
    YYEncodingType _type;        ///< property's type                   //property的encode解析值
    YYEncodingNSType _nsType;    ///< property's Foundation type        //property的foundation類型
    BOOL _isCNumber;             ///< is c number type                  //是不是c語言的數(shù)字
    Class _cls;                  ///< property's class, or nil          //property的class
    Class _genericCls;           ///< container's generic class, or nil if threr's no generic class     //property內包含的類class
    SEL _getter;                 ///< getter, or nil if the instances cannot respond        //property getter方法
    SEL _setter;                 ///< setter, or nil if the instances cannot respond        //property setter方法
    BOOL _isKVCCompatible;       ///< YES if it can access with key-value coding            //是否可以使用KVC
    BOOL _isStructAvailableForKeyedArchiver; ///< YES if the struct can encoded with keyed archiver/unarchiver      //是否是struct并且可以archiver/unarchiver
    BOOL _hasCustomClassFromDictionary; ///< class/generic class implements +modelCustomClassForDictionary:     //是否包含本本地的class轉換
    
    /*
     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                                     //property本地的key mapper的key
    NSArray *_mappedToKeyPath;   ///< the key path mapped to (nil if the name is not key path)  //property本地的key mapper的key path列表
    NSArray *_mappedToKeyArray;  ///< the key(NSString) or keyPath(NSArray) array (nil if not mapped to multiple keys) /property本地的key mapper的key列表
    YYClassPropertyInfo *_info;  ///< property's info                                   //property的YYClassPropertyInfo info
    _YYModelPropertyMeta *_next; ///< next meta if there are multiple properties mapped to the same key.      //同個key的多個property的映射next指針
}
@end

其中需要理解一下的包括:
1._hasCustomClassFromDictionary就是判斷是否有本地的不同class的映射
如下例子慷垮,當dictionary包含不同的值的時候揖闸,映射的model類型不同

@implementation YYBaseUser
+ (Class)modelCustomClassForDictionary:(NSDictionary*)dictionary {
    if (dictionary[@"localName"]) {
        return [YYLocalUser class];
    } else if (dictionary[@"remoteName"]) {
        return [YYRemoteUser class];
    }
    return [YYBaseUser class];
}

2.keyMapper相關內容
如下例子,dictionary中不同的key料身,對應的model的property有一個映射關系
其中對于包含"."的映射汤纸,則是一種多層的映射關系

+ (NSDictionary *)modelCustomPropertyMapper {
    return @{ @"name" : @"n",
              @"count" : @"ext.c",
              @"desc1" : @"ext.d", // mapped to same key path
              @"desc2" : @"ext.d", // mapped to same key path
              @"desc3" : @"ext.d.e",
              @"desc4" : @".ext",
              @"modelID" : @[@"ID", @"Id", @"id", @"ext.id"]};
}

3._YYModelPropertyMeta *_next同一個key的下一個meta解析類型
因為有了key mapper,所以會出現(xiàn)同一個mapper的key值芹血,可能會一對多個model的property值贮泞,這邊設計了一個_next指針進行鏈接

4.Class _genericCls包含的類型
如下例子,property會有NSArray\NSDictionary\NSSet類型幔烛,內部的類型就通過該參數(shù)表示

@interface YYTestCustomClassModel : NSObject
@property (nonatomic, strong) NSArray *users;
@property (nonatomic, strong) NSDictionary *userDict;
@property (nonatomic, strong) NSSet *userSet;
@end

+ (NSDictionary *)modelContainerPropertyGenericClass {
    return @{@"users" : YYBaseUser.class,
             @"userDict" : YYBaseUser.class,
             @"userSet" : YYBaseUser.class};
}

實現(xiàn)

//通過YYClassInfo啃擦,YYClassPropertyInfo,Class對象解析成_YYModelPropertyMeta
+ (instancetype)metaWithClassInfo:(YYClassInfo *)classInfo propertyInfo:(YYClassPropertyInfo *)propertyInfo generic:(Class)generic {
    
    // support pseudo generic class with protocol name
    // 這邊也考慮了饿悬,某些類型寫在protocol中令蛉,包含在<>中,和JSONModel一樣進行一下protocol的類型判斷
    // 比如NSArray<TestObject> xxx
    if (!generic && propertyInfo.protocols) {
        for (NSString *protocol in propertyInfo.protocols) {
            Class cls = objc_getClass(protocol.UTF8String);
            if (cls) {
                generic = cls;
                break;
            }
        }
    }
    
    //構造_YYModelPropertyMeta對象
    _YYModelPropertyMeta *meta = [self new];
    meta->_name = propertyInfo.name;                //設置名
    meta->_type = propertyInfo.type;                //設置encode type
    meta->_info = propertyInfo;                     //設置YYClassPropertyInfo
    meta->_genericCls = generic;                    //設置可能有的包含類
    
    if ((meta->_type & YYEncodingTypeMask) == YYEncodingTypeObject) {   //如果是OC對象狡恬,則判斷是不是標準oc foundation類型
        meta->_nsType = YYClassGetNSType(propertyInfo.cls);
    } else {
        meta->_isCNumber = YYEncodingTypeIsCNumber(meta->_type);        //判斷是否是c 數(shù)字
    }
    if ((meta->_type & YYEncodingTypeMask) == YYEncodingTypeStruct) {       //判斷和是否是struct
        /*
         It seems that NSKeyedUnarchiver cannot decode NSValue except these structs:
         */
        //NSKeyedUnarchiver只可以解析以下的struct
        static NSSet *types = nil;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            NSMutableSet *set = [NSMutableSet new];
            // 32 bit
            [set addObject:@"{CGSize=ff}"];
            [set addObject:@"{CGPoint=ff}"];
            [set addObject:@"{CGRect={CGPoint=ff}{CGSize=ff}}"];
            [set addObject:@"{CGAffineTransform=ffffff}"];
            [set addObject:@"{UIEdgeInsets=ffff}"];
            [set addObject:@"{UIOffset=ff}"];
            // 64 bit
            [set addObject:@"{CGSize=dd}"];
            [set addObject:@"{CGPoint=dd}"];
            [set addObject:@"{CGRect={CGPoint=dd}{CGSize=dd}}"];
            [set addObject:@"{CGAffineTransform=dddddd}"];
            [set addObject:@"{UIEdgeInsets=dddd}"];
            [set addObject:@"{UIOffset=dd}"];
            types = set;
        });
        if ([types containsObject:propertyInfo.typeEncoding]) {
            meta->_isStructAvailableForKeyedArchiver = YES;
        }
    }
    meta->_cls = propertyInfo.cls;      //設置類
    
    if (generic) {      //如果是包含類的話珠叔,用包含類去判斷是否需要類型轉換
        meta->_hasCustomClassFromDictionary = [generic respondsToSelector:@selector(modelCustomClassForDictionary:)];
    } else if (meta->_cls && meta->_nsType == YYEncodingTypeNSUnknown) {        //如果是其它oc類型,用類型去判斷是否需要類型轉換
        meta->_hasCustomClassFromDictionary = [meta->_cls respondsToSelector:@selector(modelCustomClassForDictionary:)];
    }
    
    //設置getter方法
    if (propertyInfo.getter) {
        if ([classInfo.cls instancesRespondToSelector:propertyInfo.getter]) {
            meta->_getter = propertyInfo.getter;
        }
    }
    
    //設置setter方法
    if (propertyInfo.setter) {
        if ([classInfo.cls instancesRespondToSelector:propertyInfo.setter]) {
            meta->_setter = propertyInfo.setter;
        }
    }
    
    //如果包含getter和setter方法
    if (meta->_getter && meta->_setter) {
        /*
         KVC invalid type:
         long double
         pointer (such as SEL/CoreFoundation object)
         */
        //以下代表可以使用kvc進行附值
        switch (meta->_type & YYEncodingTypeMask) {
            case YYEncodingTypeBool:
            case YYEncodingTypeInt8:
            case YYEncodingTypeUInt8:
            case YYEncodingTypeInt16:
            case YYEncodingTypeUInt16:
            case YYEncodingTypeInt32:
            case YYEncodingTypeUInt32:
            case YYEncodingTypeInt64:
            case YYEncodingTypeUInt64:
            case YYEncodingTypeFloat:
            case YYEncodingTypeDouble:
            case YYEncodingTypeObject:
            case YYEncodingTypeClass:
            case YYEncodingTypeBlock:
            case YYEncodingTypeStruct:
            case YYEncodingTypeUnion: {
                meta->_isKVCCompatible = YES;
            } break;
            default: break;
        }
    }
    
    return meta;
}

實現(xiàn)部分還是容易理解弟劲,主要還是通過YYClassInfo运杭,YYClassPropertyInfo,Class對象解析成_YYModelPropertyMeta函卒。

@interface _YYModelMeta : NSObject

聲明

/// A class info in object model.
// model class的進一層封裝
@interface _YYModelMeta : NSObject {
    @package
    YYClassInfo *_classInfo;            //YYClassInfo類型
    /// Key:mapped key and key path, Value:_YYModelPropertyMeta.
    NSDictionary *_mapper;              //key mapper的值辆憔,key是mapped key and key path撇眯,Value是_YYModelPropertyMeta類型
    /// Array<_YYModelPropertyMeta>, all property meta of this model.
    NSArray *_allPropertyMetas;         //所有的_YYModelPropertyMeta列表
    /// Array<_YYModelPropertyMeta>, property meta which is mapped to a key path.
    NSArray *_keyPathPropertyMetas;     //所有的路徑映射property緩存
    /// Array<_YYModelPropertyMeta>, property meta which is mapped to multi keys.
    NSArray *_multiKeysPropertyMetas;   //所有的多層映射property緩存
    /// The number of mapped key (and key path), same to _mapper.count.
    NSUInteger _keyMappedCount;         //key mapper數(shù)量
    /// Model class type.
    YYEncodingNSType _nsType;           //model class的oc類型
    
    BOOL _hasCustomWillTransformFromDictionary;     //是否包含本地的某些值進行替換
    BOOL _hasCustomTransformFromDictionary;         //是否有本地的值的類型判斷
    BOOL _hasCustomTransformToDictionary;           //與上個相反,當轉成dictionary的時候虱咧,轉換的方式
    BOOL _hasCustomClassFromDictionary;             //是否有本地的類型的轉換
}
@end

其中熊榛,需要理解的包括:

NSDictionary *_mapper;
NSArray *_allPropertyMetas; 
NSArray *_keyPathPropertyMetas; 
NSArray *_multiKeysPropertyMetas;

_mapper是包括了所有class和superclass的property的key value緩存,其中的key是所有已經映射過key name和不需要映射的property name組成腕巡。
_allPropertyMetas是所有class和superclass的property解析_YYModelPropertyMeta列表玄坦。
_keyPathPropertyMetas是表示映射如果是一個路徑比如@"count" : @"ext.c"下的_YYModelPropertyMeta列表。
_multiKeysPropertyMetas是表示映射如果是一個NSArray比如@"modelID" : @[@"ID", @"Id", @"id", @"ext.id"]下的_YYModelPropertyMeta列表绘沉。

    BOOL _hasCustomWillTransformFromDictionary;     //是否包含本地的某些值進行替換
    BOOL _hasCustomTransformFromDictionary;         //是否有本地的值的類型判斷
    BOOL _hasCustomTransformToDictionary;           //與上個相反煎楣,當轉成dictionary的時候,轉換的方式
    BOOL _hasCustomClassFromDictionary;             //是否有本地的類型的轉換

判斷方式:

    _hasCustomWillTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomWillTransformFromDictionary:)]);
    _hasCustomTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformFromDictionary:)]);
    _hasCustomTransformToDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformToDictionary:)]);
    _hasCustomClassFromDictionary = ([cls respondsToSelector:@selector(modelCustomClassForDictionary:)]);

對應的實際例子:

@interface YYTestCustomTransformModel : NSObject
@property uint64_t id;
@property NSString *content;
@property NSDate *time;
@end

-(NSDictionary *)modelCustomWillTransformFromDictionary:(NSDictionary *)dic{
    if (dic) {
        NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:dic];
        if (dict[@"date"]) {
            dict[@"time"] = dict[@"date"];
        }
        return dict;
    }
    return dic;
}

- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic {
    NSNumber *time = dic[@"time"];
    if ([time isKindOfClass:[NSNumber class]] && time.unsignedLongLongValue != 0) {
        _time = [NSDate dateWithTimeIntervalSince1970:time.unsignedLongLongValue / 1000.0];
        return YES;
    } else {
        return NO;
    }
}

- (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic {
    if (_time) {
        dic[@"time"] = @((uint64_t)(_time.timeIntervalSince1970 * 1000));
        return YES;
    } else {
        return NO;
    }
}

最后一個在_YYModelPropertyMeta已經提過车伞,這里不再提择懂。

實現(xiàn)

- (instancetype)initWithClass:(Class)cls {
    YYClassInfo *classInfo = [YYClassInfo classInfoWithClass:cls];      //構造 YYClassInfo結構體
    if (!classInfo) return nil;
    self = [super init];
    
    // Get black list
    NSSet *blacklist = nil;
    if ([cls respondsToSelector:@selector(modelPropertyBlacklist)]) {       //黑名單,不解析的property
        NSArray *properties = [(id<YYModel>)cls modelPropertyBlacklist];
        if (properties) {
            blacklist = [NSSet setWithArray:properties];
        }
    }
    
    // Get white list
    NSSet *whitelist = nil;
    if ([cls respondsToSelector:@selector(modelPropertyWhitelist)]) {       //白名單另玖,只解析的property
        NSArray *properties = [(id<YYModel>)cls modelPropertyWhitelist];
        if (properties) {
            whitelist = [NSSet setWithArray:properties];
        }
    }
    
    // Get container property's generic class
    //獲得包含的類的類型
    NSDictionary *genericMapper = nil;
    if ([cls respondsToSelector:@selector(modelContainerPropertyGenericClass)]) {
        genericMapper = [(id<YYModel>)cls modelContainerPropertyGenericClass];      //獲得包含類映射的dictionary
        if (genericMapper) {
            NSMutableDictionary *tmp = [NSMutableDictionary new];
            [genericMapper enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {        //遍歷dictionary
                if (![key isKindOfClass:[NSString class]]) return;
                Class meta = object_getClass(obj);          //獲得映射的類型
                if (!meta) return;                          //不是類型返回
                if (class_isMetaClass(meta)) {              //如果是meta class的話,直接設置(因為是對象的類類型)
                    tmp[key] = obj;
                } else if ([obj isKindOfClass:[NSString class]]) {  //如果是string的話困曙,轉成class,設置
                    Class cls = NSClassFromString(obj);
                    if (cls) {
                        tmp[key] = cls;
                    }
                }
            }];
            genericMapper = tmp;                    //獲得包含類的映射dictionary
        }
    }
    
    // Create all property metas.
    NSMutableDictionary *allPropertyMetas = [NSMutableDictionary new];
    YYClassInfo *curClassInfo = classInfo;
    
    //遞歸class和superclass谦去,但忽略nsobject/nsproxy
    while (curClassInfo && curClassInfo.superCls != nil) { // recursive parse super class, but ignore root class (NSObject/NSProxy)
        //獲得class的所有YYClassPropertyInfo緩存
        for (YYClassPropertyInfo *propertyInfo in curClassInfo.propertyInfos.allValues) {
            if (!propertyInfo.name) continue;
            if (blacklist && [blacklist containsObject:propertyInfo.name]) continue;    //跳過黑名單
            if (whitelist && ![whitelist containsObject:propertyInfo.name]) continue;   //只判斷拍名單
            //構造_YYModelPropertyMeta結構體
            _YYModelPropertyMeta *meta = [_YYModelPropertyMeta metaWithClassInfo:classInfo
                                                                    propertyInfo:propertyInfo
                                                                         generic:genericMapper[propertyInfo.name]];
            if (!meta || !meta->_name) continue;                        //沒有名字跳過
            if (!meta->_getter || !meta->_setter) continue;             //沒有getter或者setter方法跳過
            if (allPropertyMetas[meta->_name]) continue;                //已經解析過的跳過
            allPropertyMetas[meta->_name] = meta;                       //將解析通過dictionary緩存
        }
        curClassInfo = curClassInfo.superClassInfo;                 //遞歸super class
    }
    if (allPropertyMetas.count) _allPropertyMetas = allPropertyMetas.allValues.copy;    //復制一份解析出來的所有allPropertyMetas
    
    // create mapper
    //開始key mapper映射
    NSMutableDictionary *mapper = [NSMutableDictionary new];
    NSMutableArray *keyPathPropertyMetas = [NSMutableArray new];
    NSMutableArray *multiKeysPropertyMetas = [NSMutableArray new];
    
    //如果有key mapper 映射
    if ([cls respondsToSelector:@selector(modelCustomPropertyMapper)]) {
        NSDictionary *customMapper = [(id <YYModel>)cls modelCustomPropertyMapper];
        [customMapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyName, NSString *mappedToKey, BOOL *stop) {
            _YYModelPropertyMeta *propertyMeta = allPropertyMetas[propertyName];        //找到映射值的_YYModelPropertyMeta
            if (!propertyMeta) return;                              //沒有直接返回
            [allPropertyMetas removeObjectForKey:propertyName];     //有映射所以從原來的列表刪除
            
            if ([mappedToKey isKindOfClass:[NSString class]]) {             //如果是NSString映射
                if (mappedToKey.length == 0) return;
                
                propertyMeta->_mappedToKey = mappedToKey;               //設置mapper key
                NSArray *keyPath = [mappedToKey componentsSeparatedByString:@"."];  //如果是包含"."代表key的路徑映射
                for (NSString *onePath in keyPath) {
                    if (onePath.length == 0) {
                        NSMutableArray *tmp = keyPath.mutableCopy;
                        [tmp removeObject:@""];
                        keyPath = tmp;
                        break;
                    }
                }
                if (keyPath.count > 1) {                            //>1說明有路徑映射
                    propertyMeta->_mappedToKeyPath = keyPath;       //設置_mappedToKeyPath
                    [keyPathPropertyMetas addObject:propertyMeta];  //添加路徑映射對象
                }
                propertyMeta->_next = mapper[mappedToKey] ?: nil;   //如果包含上一個同樣key值的_YYModelPropertyMeta對象慷丽,則設置next指針
                mapper[mappedToKey] = propertyMeta;
                
            } else if ([mappedToKey isKindOfClass:[NSArray class]]) {       //如果是nsarray映射
                
                NSMutableArray *mappedToKeyArray = [NSMutableArray new];
                for (NSString *oneKey in ((NSArray *)mappedToKey)) {        //多一級nsarray的遍歷,內容與nsstring相同
                    if (![oneKey isKindOfClass:[NSString class]]) continue;
                    if (oneKey.length == 0) continue;
                    
                    NSArray *keyPath = [oneKey componentsSeparatedByString:@"."];
                    if (keyPath.count > 1) {
                        [mappedToKeyArray addObject:keyPath];
                    } else {
                        [mappedToKeyArray addObject:oneKey];
                    }
                    
                    if (!propertyMeta->_mappedToKey) {
                        propertyMeta->_mappedToKey = oneKey;
                        propertyMeta->_mappedToKeyPath = keyPath.count > 1 ? keyPath : nil;
                    }
                }
                if (!propertyMeta->_mappedToKey) return;
                
                propertyMeta->_mappedToKeyArray = mappedToKeyArray;     //多級的映射值會存到_mappedToKeyArray
                [multiKeysPropertyMetas addObject:propertyMeta];
                
                propertyMeta->_next = mapper[mappedToKey] ?: nil;   //同樣鳄哭,如果包含上一個同樣key值的_YYModelPropertyMeta對象要糊,則設置next指針
                mapper[mappedToKey] = propertyMeta;
            }
        }];
    }
    
    //映射完后,遍歷所有的allPropertyMetas妆丘,設置mapper和next指針
    [allPropertyMetas enumerateKeysAndObjectsUsingBlock:^(NSString *name, _YYModelPropertyMeta *propertyMeta, BOOL *stop) {
        propertyMeta->_mappedToKey = name;
        propertyMeta->_next = mapper[name] ?: nil;
        mapper[name] = propertyMeta;
    }];
    
    //如果有映射值杨耙,則緩存
    if (mapper.count) _mapper = mapper;
    //如果路徑映射值,則緩存
    if (keyPathPropertyMetas) _keyPathPropertyMetas = keyPathPropertyMetas;
    //如果有多映射值飘痛,則緩存
    if (multiKeysPropertyMetas) _multiKeysPropertyMetas = multiKeysPropertyMetas;
    
    _classInfo = classInfo;         //設置classinfo
    _keyMappedCount = _allPropertyMetas.count;          //設置property數(shù)量
    _nsType = YYClassGetNSType(cls);            //獲得本類的oc foundation類型
    _hasCustomWillTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomWillTransformFromDictionary:)]);
    _hasCustomTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformFromDictionary:)]);
    _hasCustomTransformToDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformToDictionary:)]);
    _hasCustomClassFromDictionary = ([cls respondsToSelector:@selector(modelCustomClassForDictionary:)]);
    
    return self;
}

/// Returns the cached model class meta
//返回緩存的model class meta
+ (instancetype)metaWithClass:(Class)cls {
    if (!cls) return nil;
    static CFMutableDictionaryRef cache;        //class的_YYModelMeta緩存
    static dispatch_once_t onceToken;
    static dispatch_semaphore_t lock;
    dispatch_once(&onceToken, ^{
        cache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
        lock = dispatch_semaphore_create(1);
    });
    dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
    _YYModelMeta *meta = CFDictionaryGetValue(cache, (__bridge const void *)(cls));
    dispatch_semaphore_signal(lock);
    if (!meta || meta->_classInfo.needUpdate) {             //利用
        meta = [[_YYModelMeta alloc] initWithClass:cls];    //重新構造 _YYModelMeta緩存
        if (meta) {                                         //設置緩存
            dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
            CFDictionarySetValue(cache, (__bridge const void *)(cls), (__bridge const void *)(meta));
            dispatch_semaphore_signal(lock);
        }
    }
    return meta;
}

實現(xiàn)處理了很多key mapper的映射問題珊膜,但本身邏輯并不復雜。

解析邏輯

//id json對象轉化為 dictioanry的方法
+ (NSDictionary *)_yy_dictionaryWithJSON:(id)json {
    if (!json || json == (id)kCFNull) return nil;
    NSDictionary *dic = nil;
    NSData *jsonData = nil;
    if ([json isKindOfClass:[NSDictionary class]]) {
        dic = json;
    } else if ([json isKindOfClass:[NSString class]]) {
        jsonData = [(NSString *)json dataUsingEncoding : NSUTF8StringEncoding];
    } else if ([json isKindOfClass:[NSData class]]) {
        jsonData = json;
    }
    if (jsonData) {
        dic = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:NULL];
        if (![dic isKindOfClass:[NSDictionary class]]) dic = nil;
    }
    return dic;
}

//先轉化json對象到dictionary宣脉,再調用yy_modelWithDictionary
+ (instancetype)yy_modelWithJSON:(id)json {
    NSDictionary *dic = [self _yy_dictionaryWithJSON:json];
    return [self yy_modelWithDictionary:dic];
}

//解析model屬性并附值
+ (instancetype)yy_modelWithDictionary:(NSDictionary *)dictionary {
    if (!dictionary || dictionary == (id)kCFNull) return nil;
    if (![dictionary isKindOfClass:[NSDictionary class]]) return nil;
    
    Class cls = [self class];
    //解析class得到modelmeta對象
    _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:cls];
    //本地class類型映射
    if (modelMeta->_hasCustomClassFromDictionary) {
        cls = [cls modelCustomClassForDictionary:dictionary] ?: cls;
    }
    
    NSObject *one = [cls new];
    //附值函數(shù)
    if ([one yy_modelSetWithDictionary:dictionary]) return one;
    return nil;
}


//附值函數(shù)
- (BOOL)yy_modelSetWithDictionary:(NSDictionary *)dic {
    if (!dic || dic == (id)kCFNull) return NO;
    if (![dic isKindOfClass:[NSDictionary class]]) return NO;
    

    _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:object_getClass(self)];
    if (modelMeta->_keyMappedCount == 0) return NO;
    
    //本地dictionary值替換
    if (modelMeta->_hasCustomWillTransformFromDictionary) {
        dic = [((id<YYModel>)self) modelCustomWillTransformFromDictionary:dic];
        if (![dic isKindOfClass:[NSDictionary class]]) return NO;
    }
    
    ModelSetContext context = {0};
    context.modelMeta = (__bridge void *)(modelMeta);
    context.model = (__bridge void *)(self);
    context.dictionary = (__bridge void *)(dic);
    
    
    //遍歷dictioanry并附值
    if (modelMeta->_keyMappedCount >= CFDictionaryGetCount((CFDictionaryRef)dic)) {
        CFDictionaryApplyFunction((CFDictionaryRef)dic, ModelSetWithDictionaryFunction, &context);
        if (modelMeta->_keyPathPropertyMetas) {
            CFArrayApplyFunction((CFArrayRef)modelMeta->_keyPathPropertyMetas,
                                 CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_keyPathPropertyMetas)),
                                 ModelSetWithPropertyMetaArrayFunction,
                                 &context);
        }
        if (modelMeta->_multiKeysPropertyMetas) {
            CFArrayApplyFunction((CFArrayRef)modelMeta->_multiKeysPropertyMetas,
                                 CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_multiKeysPropertyMetas)),
                                 ModelSetWithPropertyMetaArrayFunction,
                                 &context);
        }
    } else {
        CFArrayApplyFunction((CFArrayRef)modelMeta->_allPropertyMetas,
                             CFRangeMake(0, modelMeta->_keyMappedCount),
                             ModelSetWithPropertyMetaArrayFunction,
                             &context);
    }
    
    //本地property驗證
    if (modelMeta->_hasCustomTransformFromDictionary) {
        return [((id<YYModel>)self) modelCustomTransformFromDictionary:dic];
    }
    return YES;
}

附值這塊车柠,就只取ModelSetWithDictionaryFunction設置

static void ModelSetWithDictionaryFunction(const void *_key, const void *_value, void *_context) {
    ModelSetContext *context = _context;
    __unsafe_unretained _YYModelMeta *meta = (__bridge _YYModelMeta *)(context->modelMeta); //取_YYModelMeta
    __unsafe_unretained _YYModelPropertyMeta *propertyMeta = [meta->_mapper objectForKey:(__bridge id)(_key)];  //取_YYModelPropertyMeta
    __unsafe_unretained id model = (__bridge id)(context->model);
    while (propertyMeta) {          //循環(huán)遍歷propertyMeta
        if (propertyMeta->_setter) {
            ModelSetValueForProperty(model, (__bridge __unsafe_unretained id)_value, propertyMeta); //設置值
        }
        propertyMeta = propertyMeta->_next;
    };
}

ModelSetValueForProperty的設置函數(shù),就是根據(jù)不同類型塑猖,去調用msgSend調用相應的meta->_setter方法竹祷,進行附值,代碼較長羊苟,這里也不貼了塑陵。

至此,整個YYModel的解析就完成了蜡励。

總結

YYModel在整體的解析過程中令花,分三步:

  1. 先將Class的Method阻桅,Property,Ivar分別解析緩存兼都,構成Class的緩存
  2. 構建_YYModelPropertyMeta緩存Property結構體嫂沉,再_YYModelMeta緩存_YYModelPropertyMeta結構。
    3.根據(jù)解析出來的_YYModelPropertyMeta與相應的YYEncodingType等屬性扮碧,進行相應值的設置

JSONModel與YYModel對比

對比 JSONModel YYModel
解析方式 只解析property attribute encode string 解析Class Method,Property,Ivar緩存趟章,再構造_YYModelPropertyMeta_YYModelMeta
附值方式 KVC 先解析出property的類型,使用msgSend調用meta->setter方法分類型附值
緩存方式 AssociatedObject CFMutableDictionaryRef
包含類型方式 定義同名protocol慎王,聲明protocol 不僅可以聲明protocol定義蚓土,還可以實現(xiàn)modelContainerPropertyGenericClass接口
映射值為路徑解析
model類型轉換
懶加載
是否可以缺省 通過protocol optional 黑名單\白名單

參考資料

CSDN地址
1.JSONModel源代碼解析
2.鄭欽洪_:YYModel 源碼歷險記

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市赖淤,隨后出現(xiàn)的幾起案子蜀漆,更是在濱河造成了極大的恐慌,老刑警劉巖漫蛔,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異旧蛾,居然都是意外死亡莽龟,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進店門锨天,熙熙樓的掌柜王于貴愁眉苦臉地迎上來毯盈,“玉大人,你說我怎么就攤上這事病袄÷Ц常” “怎么了?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵益缠,是天一觀的道長脑奠。 經常有香客問我,道長幅慌,這世上最難降的妖魔是什么宋欺? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮胰伍,結果婚禮上齿诞,老公的妹妹穿的比我還像新娘。我一直安慰自己骂租,他們只是感情好祷杈,可當我...
    茶點故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著渗饮,像睡著了一般但汞。 火紅的嫁衣襯著肌膚如雪宿刮。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天特占,我揣著相機與錄音糙置,去河邊找鬼。 笑死是目,一個胖子當著我的面吹牛谤饭,可吹牛的內容都是我干的。 我是一名探鬼主播懊纳,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼揉抵,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了嗤疯?” 一聲冷哼從身側響起冤今,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎茂缚,沒想到半個月后戏罢,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡脚囊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年龟糕,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片悔耘。...
    茶點故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡讲岁,死狀恐怖,靈堂內的尸體忽然破棺而出衬以,到底是詐尸還是另有隱情缓艳,我是刑警寧澤,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布看峻,位于F島的核電站阶淘,受9級特大地震影響,放射性物質發(fā)生泄漏互妓。R本人自食惡果不足惜舶治,卻給世界環(huán)境...
    茶點故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望车猬。 院中可真熱鬧霉猛,春花似錦、人聲如沸珠闰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽伏嗜。三九已至坛悉,卻和暖如春伐厌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背裸影。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工挣轨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人轩猩。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓卷扮,卻偏偏與公主長得像,于是被迫代替她去往敵國和親均践。 傳聞我的和親對象是個殘疾皇子晤锹,可洞房花燭夜當晚...
    茶點故事閱讀 44,960評論 2 355

推薦閱讀更多精彩內容

  • 前言 本文的中文注釋代碼demo更新在我的github上。 上篇 YYModel源代碼分析(一)整體介紹 主要寫了...
    game3108閱讀 2,430評論 1 51
  • 終于把前面的base文件夾簡簡單單的看了一遍彤委,終于可以回到正片上來了鞭铆,保證不爛尾。 項目天天用yymodel解析數(shù)...
    充滿活力的早晨閱讀 1,368評論 1 0
  • 上面一篇文章沒有分析完yymodel 焦影。 接著上篇接著分析 static void ModelSetValueFo...
    充滿活力的早晨閱讀 1,059評論 0 0
  • 前言 本文的demo更新在github上车遂。 客戶端請求服務器,經常使用的時JSON方式傳遞數(shù)據(jù)斯辰。一些第三方開源庫幫...
    game3108閱讀 3,168評論 3 53
  • (稻盛哲學學習會)打卡第81天 姓名:汪何炯 部門:品控部 組別:待定 【知~學習】 誦讀《活法》第五章 災難消“...
    汪何炯閱讀 1,051評論 0 0