YYModel源码分析(三)

话不多说,下面直接开始分析设置值的核心方法,代码如下:

- (BOOL)modelSetWithDictionary:(NSDictionary *)dic {
    if (!dic || dic == (id)kCFNull) return NO;
    if (![dic isKindOfClass:[NSDictionary class]]) return NO;
    // 模型元数据
    _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:object_getClass(self)];
    // 没有key->property直接返回
    if (modelMeta->_keyMappedCount == 0) return NO;
    
    // 在这里能修改数据源字典
    if (modelMeta->_hasCustomWillTransformFromDictionary) {
        dic = [((id)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);
    
    // 为属性设值
    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);
    }
    
    // 返回YES表明该模型可用,返回NO忽略该模型
    if (modelMeta->_hasCustomTransformFromDictionary) {
        return [((id)self) modelCustomTransformFromDictionary:dic];
    }
    return YES;
}

其中ModelSetContext为作者定义的结构体,如下:

typedef struct {
    void *modelMeta;  ///< _YYModelMeta
    void *model;      ///< id (self)
    void *dictionary; ///< NSDictionary (json)
} ModelSetContext;
  • modelMeta 表示模型元数据
  • model 表示模型本身
  • dictionary 表示数据源字典
    此处有两个分支,当model的key->property大于等于数据源字典的key、value数量时,分别设置keyPath、多个key对应同一属性的值;另外一种情况直接设置所有的属性元数据的值。
    CFDictionaryApplyFunction为Core Foundation API,会为每个key/value对调用一次指定的方法。
    CFArrayApplyFunction会为数组内指定范围的每个元素调用一次指定的方法。
static void ModelSetWithDictionaryFunction(const void *_key, const void *_value, void *_context) {
    ModelSetContext *context = _context;
    __unsafe_unretained _YYModelMeta *meta = (__bridge _YYModelMeta *)(context->modelMeta);
    __unsafe_unretained _YYModelPropertyMeta *propertyMeta = [meta->_mapper objectForKey:(__bridge id)(_key)];
    __unsafe_unretained id model = (__bridge id)(context->model);
    // 多个property对应一个key,循环赋值
    while (propertyMeta) {
        if (propertyMeta->_setter) {
            // 赋值方法
            ModelSetValueForProperty(model, (__bridge __unsafe_unretained id)_value, propertyMeta);
        }
        propertyMeta = propertyMeta->_next;
    };
}

关于赋值方法ModelSetValueForProperty没有什么好讲的,就是根据model属性的类型来相应的赋值,其中会有一些字符串转换日期,字符串转换NSNumber等的方法调用。

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) {
        // 多个key对应同一属性赋值
        value = YYValueForMultiKeys(dictionary, propertyMeta->_mappedToKeyArray);
    } else if (propertyMeta->_mappedToKeyPath) {
        // keyPath赋值
        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);
    }
}

可以看到,多个key对应同一属性的优先级最高,keyPath其次,这样在返回自定义key->property对于同一属性多次设置时会忽略优先级低的赋值。

你可能感兴趣的:(YYModel源码分析(三))