YYModel原理分析

266 阅读3分钟
NSDictionary *dict = @{@"subModel":@{@"name":@"Nomy",
                                 @"age":@5},
                           @"name":@"Arron",
                           @"age":@33,
                           @"infoDict":@{@"submodel":@{@"gender":@1,@"name":@"YYmodel"}},
                           };
    
    
    XLModel *model = [XLModel yy_modelWithDictionary:dict];

YYModel 是进行键值编码,做字典转模型的事情。 分析对象:是对象Model包括Ivar成员变量 结果返回一个值

    Class cls = [self class];
    _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:cls];

首先,我将分析的对象类传进来,准备我们当前需要操作的对象

/// Returns the cached model class meta
+ (instancetype)metaWithClass:(Class)cls {
    if (!cls) return nil;
    static CFMutableDictionaryRef cache;
    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];
        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;
}

YYModel之所以性能高,是由于做了一些类型系统级别的缓存处理,如果我们对象类做过字典转模型的处理后,他就会缓存到通过 CFMutableDictionaryRef创建一个字典中。再次使用就会直接从cache中拿出来,通过CFDictionaryGetValue。

如果没有cache,接下来看一下是如何处理metaClass的

`- (instancetype)initWithClass:(Class)cls { YYClassInfo *classInfo = [YYClassInfo classInfoWithClass:cls]; if (!classInfo) return nil; self = [super init];

1. 处理黑名单
// Get black list 
NSSet *blacklist = nil;
if ([cls respondsToSelector:@selector(modelPropertyBlacklist)]) {
    NSArray *properties = [(id<YYModel>)cls modelPropertyBlacklist];
    if (properties) {
        blacklist = [NSSet setWithArray:properties];
    }
}

2. 处理白名单
// Get white list
NSSet *whitelist = nil;
if ([cls respondsToSelector:@selector(modelPropertyWhitelist)]) {
    NSArray *properties = [(id<YYModel>)cls modelPropertyWhitelist];
    if (properties) {
        whitelist = [NSSet setWithArray:properties];
    }
}

3. 属性替换-处理特殊集合或其他的类型
// Get container property's generic class
NSDictionary *genericMapper = nil;
if ([cls respondsToSelector:@selector(modelContainerPropertyGenericClass)]) {
    genericMapper = [(id<YYModel>)cls modelContainerPropertyGenericClass];
    if (genericMapper) {
        NSMutableDictionary *tmp = [NSMutableDictionary new];
        [genericMapper enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
            if (![key isKindOfClass:[NSString class]]) return;
            Class meta = object_getClass(obj);
            if (!meta) return;
            if (class_isMetaClass(meta)) {
                tmp[key] = obj;
            } else if ([obj isKindOfClass:[NSString class]]) {
                Class cls = NSClassFromString(obj);
                if (cls) {
                    tmp[key] = cls;
                }
            }
        }];
        genericMapper = tmp;
    }
}

4. 处理所有元类的属性
// Create all property metas.
NSMutableDictionary *allPropertyMetas = [NSMutableDictionary new];
YYClassInfo *curClassInfo = classInfo;
while (curClassInfo && curClassInfo.superCls != nil) { // recursive parse super class, but ignore root class (NSObject/NSProxy)
    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 *meta = [_YYModelPropertyMeta metaWithClassInfo:classInfo
                                                                propertyInfo:propertyInfo
                                                                     generic:genericMapper[propertyInfo.name]];
        if (!meta || !meta->_name) continue;
        if (!meta->_getter || !meta->_setter) continue;
        if (allPropertyMetas[meta->_name]) continue;
        allPropertyMetas[meta->_name] = meta;
    }
    curClassInfo = curClassInfo.superClassInfo;
}
if (allPropertyMetas.count) _allPropertyMetas = allPropertyMetas.allValues.copy;

5. 遍历类和父类的所有属性,存放到map中
// create mapper
NSMutableDictionary *mapper = [NSMutableDictionary new];
NSMutableArray *keyPathPropertyMetas = [NSMutableArray new];
NSMutableArray *multiKeysPropertyMetas = [NSMutableArray new];

if ([cls respondsToSelector:@selector(modelCustomPropertyMapper)]) {
    NSDictionary *customMapper = [(id <YYModel>)cls modelCustomPropertyMapper];
    [customMapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyName, NSString *mappedToKey, BOOL *stop) {
        _YYModelPropertyMeta *propertyMeta = allPropertyMetas[propertyName];
        if (!propertyMeta) return;
        [allPropertyMetas removeObjectForKey:propertyName];
        
        if ([mappedToKey isKindOfClass:[NSString class]]) {
            if (mappedToKey.length == 0) return;
            
            propertyMeta->_mappedToKey = mappedToKey;
            NSArray *keyPath = [mappedToKey componentsSeparatedByString:@"."];
            for (NSString *onePath in keyPath) {
                if (onePath.length == 0) {
                    NSMutableArray *tmp = keyPath.mutableCopy;
                    [tmp removeObject:@""];
                    keyPath = tmp;
                    break;
                }
            }
            if (keyPath.count > 1) {
                propertyMeta->_mappedToKeyPath = keyPath;
                [keyPathPropertyMetas addObject:propertyMeta];
            }
            propertyMeta->_next = mapper[mappedToKey] ?: nil;
            mapper[mappedToKey] = propertyMeta;
            
        } else if ([mappedToKey isKindOfClass:[NSArray class]]) {
            
            NSMutableArray *mappedToKeyArray = [NSMutableArray new];
            for (NSString *oneKey in ((NSArray *)mappedToKey)) {
                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;
            [multiKeysPropertyMetas addObject:propertyMeta];
            
            propertyMeta->_next = mapper[mappedToKey] ?: nil;
            mapper[mappedToKey] = propertyMeta;
        }
    }];
}

[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;
_keyMappedCount = _allPropertyMetas.count;
_nsType = YYClassGetNSType(cls);
_hasCustomWillTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomWillTransformFromDictionary:)]);
_hasCustomTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformFromDictionary:)]);
_hasCustomTransformToDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformToDictionary:)]);
_hasCustomClassFromDictionary = ([cls respondsToSelector:@selector(modelCustomClassForDictionary:)]);

return self;

}`

metaClass准备好后,接下来通过当前对象new新的对象,都是通过发送setter消息进行赋值

    NSObject *one = [cls new];
    if ([one yy_modelSetWithDictionary:dictionary]) return one;
    
    if (value) {
        __unsafe_unretained id model = (__bridge id)(context->model);
        ModelSetValueForProperty(model, value, propertyMeta);
    }