美文网首页技术分享
YYModel源码分析(三)

YYModel源码分析(三)

作者: fire_fire | 来源:发表于2016-06-27 14:51 被阅读247次

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

    - (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<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);
        
        // 为属性设值
        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<YYModel>)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源码分析(三)

        本文链接:https://www.haomeiwen.com/subject/zfmzdttx.html