zoukankan      html  css  js  c++  java
  • YYModel 源码解读(二)之NSObject+YYModel.h (4)

    接下来我们继续向下看

    typedef struct {
        void *modelMeta;  ///< _YYModelMeta
        void *model;      ///< id (self)
        void *dictionary; ///< NSDictionary (json)
    } ModelSetContext;

    这是一个c的结构体,在c中 void * 相当于 oc 中的 id 类型

    那么 为什么使用c的结构体呢,最主要的使用场景就是我们需要同时使用多个参数的情况下,可以使用c的结构体

    /**
     Apply function for dictionary, to set the key-value pair to model.
     
     @param _key     should not be nil, NSString.
     @param _value   should not be nil.
     @param _context _context.modelMeta and _context.model should not be nil.
     */
    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);
        while (propertyMeta) {
            if (propertyMeta->_setter) {
                ModelSetValueForProperty(model, (__bridge __unsafe_unretained id)_value, propertyMeta);
            }
            propertyMeta = propertyMeta->_next;
        };
    }

    上边的代码的主要作用是 根据 一个 id 类型的_value 一个 id 类型的_key 和_context 结构体信息,社会value 到相应的属性中

    /**
     Apply function for model property meta, to set dictionary to model.
     
     @param _propertyMeta should not be nil, _YYModelPropertyMeta.
     @param _context      _context.model and _context.dictionary should not be nil.
     */
    static void ModelSetWithPropertyMetaArrayFunction(const void *_propertyMeta, void *_context) {
        ModelSetContext *context = _context;
        
        // 这个dictionary 是 json 字典
        __unsafe_unretained NSDictionary *dictionary = (__bridge NSDictionary *)(context->dictionary);
        __unsafe_unretained _YYModelPropertyMeta *propertyMeta = (__bridge _YYModelPropertyMeta *)(_propertyMeta);
        if (!propertyMeta->_setter) return;
        id value = nil;
        
        // 如果property 映射了 多个jsonkey
        if (propertyMeta->_mappedToKeyArray) {
            // 这个是之前的函数,目的是根据字典和映射的jsonkey 取出对应的值,当获取到第一个不为空的值的情况下,停止遍历
            value = YYValueForMultiKeys(dictionary, propertyMeta->_mappedToKeyArray);
            
            
        } else if (propertyMeta->_mappedToKeyPath) {
            
            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);
        }
    }

    上边的代码也是一个赋值的函数,在一种propertyMeta 和context 的参数的情况下 ,实现的赋值方法

    接下来的这个方法主要是mode to json  模型转字典的核心方法,目的是对model 进行预处理

    注意,该方法只是对一个NSObject对象做预处理,并没有转JSON对象
    
    苹果 规定使用 NSJSONSerialization 转换成JSONObject的要求:
    
    NSArray 对象
    数组元素只能是 NSString、NSNumber、NSNull
    NSDictionary 对象
    key 必须是 NSString
    value只能是 NSString、NSNumber、NSNull
    该函数对传入的NSObject对象为如下所有类型时做的预处理:
    
    NSData >>> 不能转换成JSON
    
    NSString >>> NSString
    
    NSNumber >>> NSNumber
    NSURL >>> NSString
    NSAttributedString >>> NSString
    NSDate >>> 使用DateFormat转换成NSString
    
    NSArray
    
    先判断NSArray是否可以被JSON化
    如果可以,直接返回NSArray
    如果不可以
    创建一个新的NSArray
    遍历之前NSArray中的每一个元素
    递归将当前元素解析成 NSString、NSNumber
    将解析后的元素添加到数组
    返回新的数组
    NSSet
    NSSet >>> NSArray
    走NSArray的流程
    NSDictionary
    类似NSArray
    自定义NSObject类,非Foundation类
    将当前实体类对象 >>> 使用NSDictionary对象来重新组装
    属性值 >>> NSDictionary字典key-value
    最终 实体类对象 >>> NSDictionary对象
    /**
     Returns a valid JSON object (NSArray/NSDictionary/NSString/NSNumber/NSNull), 
     or nil if an error occurs.
     
     @param model Model, can be nil.
     @return JSON object, nil if an error occurs.
     */
    /**
     *  说明该方法主要使用在模型转字典的功能中
     *
     *  @param model 模型
     *
     *  @return 与转换后的值,因为有些值是需要处理的
     */
    static id ModelToJSONObjectRecursive(NSObject *model) {
        
        // 1.kCFNull 或者 Null
        if (!model || model == (id)kCFNull) return model;
        
        // 2.NSString 支持转json 直接返回该值
        if ([model isKindOfClass:[NSString class]]) return model;
        
        // 3.NSNumber 支持转json 直接返回该值
        if ([model isKindOfClass:[NSNumber class]]) return model;
        
        // 4.NSDictionary value 必须是NSString、NSNumber、NSNull key 必须是NSString 才支持转json 需要进行判断
        if ([model isKindOfClass:[NSDictionary class]]) {
            
            // 4.1 调用NSJSONSerialization 的 isValidJSONObject: 方法 检测能否直接转json
            if ([NSJSONSerialization isValidJSONObject:model]) return model;
            
            // 4.2 不能直接转json的情况,这个时候需要新建一个可变字典对象,转换成功后转换该字典
            NSMutableDictionary *newDic = [NSMutableDictionary new];
            [((NSDictionary *)model) enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
                
                // 4.2.1  对key的处理,NSDictionary的key 可以自定义,因此还有这种额外的情况
                NSString *stringKey = [key isKindOfClass:[NSString class]] ? key : key.description;
                if (!stringKey) return;
                
                // 4.2.2 通过递归的方式(调用自身) 预处理 字典中的value
                id jsonObj = ModelToJSONObjectRecursive(obj);
                if (!jsonObj) jsonObj = (id)kCFNull;
                
                // 4.2.3 使用处理过的 key value 给字典赋值
                newDic[stringKey] = jsonObj;
            }];
            return newDic;
        }
        
        // 5.集合,数组转json 跟字典一样 都要求是NSString、NSNumber、NSNull
        if ([model isKindOfClass:[NSSet class]]) {
            
            // 5.1 NSSet 转换成NSArray
            NSArray *array = ((NSSet *)model).allObjects;
            
            // 5.2  如果能直接转,直接返回就行了
            if ([NSJSONSerialization isValidJSONObject:array]) return array;
            
            // 5.3 这里是不能直接转的情况
            NSMutableArray *newArray = [NSMutableArray new];
            for (id obj in array) {
                
                // 5.3.1 如果是NSString 或者 NSNumber 类型,可以直接转换
                if ([obj isKindOfClass:[NSString class]] || [obj isKindOfClass:[NSNumber class]]) {
                    [newArray addObject:obj];
                }
                
                // 5.3.2  其他的 调用自身 预处理
                else {
                    id jsonObj = ModelToJSONObjectRecursive(obj);
                    if (jsonObj && jsonObj != (id)kCFNull) [newArray addObject:jsonObj];
                }
            }
            return newArray;
        }
        
        // 6. 同上 5 再次不做多余的解释
        if ([model isKindOfClass:[NSArray class]]) {
            if ([NSJSONSerialization isValidJSONObject:model]) return model;
            NSMutableArray *newArray = [NSMutableArray new];
            for (id obj in (NSArray *)model) {
                if ([obj isKindOfClass:[NSString class]] || [obj isKindOfClass:[NSNumber class]]) {
                    [newArray addObject:obj];
                } else {
                    id jsonObj = ModelToJSONObjectRecursive(obj);
                    if (jsonObj && jsonObj != (id)kCFNull) [newArray addObject:jsonObj];
                }
            }
            return newArray;
        }
        
        // 7. 对 NSURL 转成NSString
        if ([model isKindOfClass:[NSURL class]]) return ((NSURL *)model).absoluteString;
        
        // 8. 对NSAttributedString 转成NSString
        if ([model isKindOfClass:[NSAttributedString class]]) return ((NSAttributedString *)model).string;
        
        // 9. 对NSDate 转成NSString
        if ([model isKindOfClass:[NSDate class]]) return [YYISODateFormatter() stringFromDate:(id)model];
        
        // 10. 对NSData 不做处理,直接返回nil
        if ([model isKindOfClass:[NSData class]]) return nil;
        
        // 11. 如果不是上边的类型,就说明使用了自定义的类型。需要把自定义的类型转成NSDictionary 处理
        
        // 11.1  根据类型Model 初始化一个_YYModelMeta
        _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:[model class]];
        if (!modelMeta || modelMeta->_keyMappedCount == 0) return nil;
        
        // 11.2 新建一个数组,使用__unsafe_unretained 来避免 在block 中的 retain 和 release
        NSMutableDictionary *result = [[NSMutableDictionary alloc] initWithCapacity:64];
        __unsafe_unretained NSMutableDictionary *dic = result; // avoid retain and release in block
        
        // 11.3 遍历modelMeta->_mapper 这个字典 得到 jsonkey 和 _YYModelPropertyMeta描述
        [modelMeta->_mapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyMappedKey, _YYModelPropertyMeta *propertyMeta, BOOL *stop) {
            
            // 11.3.1 判空
            if (!propertyMeta->_getter) return;
            
            // 11.3.2 value 用来接收转换后的值
            id value = nil;
            
            // 11.3.3 如果是_isCNumber
            if (propertyMeta->_isCNumber) {
                
                // 11.3.3.1  通过调用ModelCreateNumberFromProperty 函数 获得转换后的NSNumber值
                value = ModelCreateNumberFromProperty(model, propertyMeta);
            }
            // 11.3.4 如果是_nsType 类型
            else if (propertyMeta->_nsType) {
                
                // 11.3.4.1 转换
                id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
                value = ModelToJSONObjectRecursive(v);
            }
            // 11.3.5 其他的情况
            else {
                
                // 11.3.5.1 判断propertyMeta->_type 根据不同的类型转换成NSString 类型
                switch (propertyMeta->_type & 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;
            
            // 到此 我们 已经获取到了 转换成功后的 value 了
            // 11.3.6 处理keypath 的情况
            /**
             *  将一个实体类对象属性值,按照json keypath,转换成对应的字典
             *  @"AA" --映射-- @"user.name"
             *
             *  还原成
             *
             *  @{
                   @"id" : 20 ,
             *     @"user" : @{
             *           @"name" : @"AA",
             *       }
             *  }
             */
            if (propertyMeta->_mappedToKeyPath) {
                NSMutableDictionary *superDic = dic;
                NSMutableDictionary *subDic = nil;
                
                // 便利keypath数组 上边的例子中 [@"user",@"name"]
                for (NSUInteger i = 0, max = propertyMeta->_mappedToKeyPath.count; i < max; i++) {
                    
                    // 11.3.6.1 取出数组中国的key
                    NSString *key = propertyMeta->_mappedToKeyPath[i];
                    
                    // 11.3.6.2 如果便利到最后一个的时候,对
                    if (i + 1 == max) { // end
                        if (!superDic[key]) superDic[key] = value;
                        break;
                    }
                    
                    // 11.3.6.3 给最上边的父类 赋值  上边的例子中dic[@"user"] ==  subDic
                    subDic = superDic[key];
                    if (subDic) {
                        if ([subDic isKindOfClass:[NSDictionary class]]) {
                            subDic = subDic.mutableCopy;
                            superDic[key] = subDic;
                        } else {
                            break;
                        }
                    } else {
                        subDic = [NSMutableDictionary new];
                        superDic[key] = subDic;
                    }
                    superDic = subDic;
                    subDic = nil;
                    
                    /*
                      总之,上边这个循环的处理目的就是给字典中keypath 映射的模型中的属性赋值
                      如果 映射的模型中的该属性有值就不赋值了
                     */
                }
            } else {
                if (!dic[propertyMeta->_mappedToKey]) {
                    dic[propertyMeta->_mappedToKey] = value;
                }
            }
        }];
        
        // 11.4 判断该类是否实现了modelCustomTransformToDictionary 方法
        if (modelMeta->_hasCustomTransformToDictionary) {
            BOOL suc = [((id<YYModel>)model) modelCustomTransformToDictionary:dic];
            if (!suc) return nil;
        }
        return result;
    }

    下边的代码是控制行的缩进

    NSMutableString *str = 可变的@"xxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxx";

    显示是这样的

    xxxxxxxxxxxxxxxxxxxxxxxx

    xxxxxxxxxxxxxxxxxxxxxxxx

    xxxxxxxxxxxxxxxxxxxxxxxx

    ModelDescriptionAddIndent(str,1) 的结果就是这样的

    xxxxxxxxxxxxxxxxxxxxxxxx

          xxxxxxxxxxxxxxxxxxxxxxxx

          xxxxxxxxxxxxxxxxxxxxxxxx

    /// Add indent to string (exclude first line)
    // 这个函数的目的就是除了第一行,其他的行都缩进 indent * 4 个 字符
    static NSMutableString *ModelDescriptionAddIndent(NSMutableString *desc, NSUInteger indent) {
        for (NSUInteger i = 0, max = desc.length; i < max; i++) {
            unichar c = [desc characterAtIndex:i];
            if (c == '
    ') {
                for (NSUInteger j = 0; j < indent; j++) {
                    [desc insertString:@"    " atIndex:i + 1];
                }
                i += indent * 4;
                max += indent * 4;
            }
        }
        return desc;
    }

    类似系统的获取对这个模型的描述

    /// Generate a description string
    static NSString *ModelDescription(NSObject *model) {
        
        // 1. 设置描述的最大长度为100
        static const int kDescMaxLength = 100;
        
        // 2. 进行判空或者不是NSObject的处理
        if (!model) return @"<nil>";
        if (model == (id)kCFNull) return @"<null>";
        if (![model isKindOfClass:[NSObject class]]) return [NSString stringWithFormat:@"%@",model];
        
        // 3. 获取modelMeta的抽象类
        _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:model.class];
        
        // 3.1 使用抽象类的类型
        switch (modelMeta->_nsType) {
                
                // 3.1.1 字符串
            case YYEncodingTypeNSString: case YYEncodingTypeNSMutableString: {
                return [NSString stringWithFormat:@""%@"",model];
            }
            
                // 3.1.2 NSVale 或者NSData
            case YYEncodingTypeNSValue:
            case YYEncodingTypeNSData: case YYEncodingTypeNSMutableData: {
                NSString *tmp = model.description;
                
                // 处理超过最大描述长度
                if (tmp.length > kDescMaxLength) {
                    tmp = [tmp substringToIndex:kDescMaxLength];
                    tmp = [tmp stringByAppendingString:@"..."];
                }
                return tmp;
            }
                
                // 3.1.3 NSNumber NSDate NSURL
            case YYEncodingTypeNSNumber:
            case YYEncodingTypeNSDecimalNumber:
            case YYEncodingTypeNSDate:
            case YYEncodingTypeNSURL: {
                return [NSString stringWithFormat:@"%@",model];
            }
                
                // 3.1.4 NSSet
            case YYEncodingTypeNSSet: case YYEncodingTypeNSMutableSet: {
                model = ((NSSet *)model).allObjects;
            } // no break
                
                // 3.1.5 NSArray
            case YYEncodingTypeNSArray: case YYEncodingTypeNSMutableArray: {
                NSArray *array = (id)model;
                
                // 便利数组,逐行打印数组内部的类型
                NSMutableString *desc = [NSMutableString new];
                if (array.count == 0) {
                    return [desc stringByAppendingString:@"[]"];
                } else {
                    [desc appendFormat:@"[
    "];
                    for (NSUInteger i = 0, max = array.count; i < max; i++) {
                        NSObject *obj = array[i];
                        [desc appendString:@"    "];
                        [desc appendString:ModelDescriptionAddIndent(ModelDescription(obj).mutableCopy, 1)];
                        [desc appendString:(i + 1 == max) ? @"
    " : @";
    "];
                    }
                    [desc appendString:@"]"];
                    return desc;
                }
            }
                
                // 3.1.6  NSDictionary
            case YYEncodingTypeNSDictionary: case YYEncodingTypeNSMutableDictionary: {
                NSDictionary *dic = (id)model;
                
                // 便利字典,逐行打印字典的key value
                NSMutableString *desc = [NSMutableString new];
                if (dic.count == 0) {
                    return [desc stringByAppendingString:@"{}"];
                } else {
                    NSArray *keys = dic.allKeys;
                    
                    [desc appendFormat:@"{
    "];
                    for (NSUInteger i = 0, max = keys.count; i < max; i++) {
                        NSString *key = keys[i];
                        NSObject *value = dic[key];
                        [desc appendString:@"    "];
                        [desc appendFormat:@"%@ = %@",key, ModelDescriptionAddIndent(ModelDescription(value).mutableCopy, 1)];
                        [desc appendString:(i + 1 == max) ? @"
    " : @";
    "];
                    }
                    [desc appendString:@"}"];
                }
                return desc;
            }
            
                // 3.1.7 自定义的类型
            default: {
                
                // 如果没有属性,则打印内存地址
                NSMutableString *desc = [NSMutableString new];
                [desc appendFormat:@"<%@: %p>", model.class, model];
                if (modelMeta->_allPropertyMetas.count == 0) return desc;
                
                // sort property names 按名称排序
                NSArray *properties = [modelMeta->_allPropertyMetas
                                       sortedArrayUsingComparator:^NSComparisonResult(_YYModelPropertyMeta *p1, _YYModelPropertyMeta *p2) {
                                           return [p1->_name compare:p2->_name];
                                       }];
                
                [desc appendFormat:@" {
    "];
                for (NSUInteger i = 0, max = properties.count; i < max; i++) {
                    _YYModelPropertyMeta *property = properties[i];
                    NSString *propertyDesc;
                    if (property->_isCNumber) {
                        NSNumber *num = ModelCreateNumberFromProperty(model, property);
                        propertyDesc = num.stringValue;
                    } else {
                        switch (property->_type & YYEncodingTypeMask) {
                            case YYEncodingTypeObject: {
                                id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter);
                                propertyDesc = ModelDescription(v);
                                if (!propertyDesc) propertyDesc = @"<nil>";
                            } break;
                            case YYEncodingTypeClass: {
                                id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter);
                                propertyDesc = ((NSObject *)v).description;
                                if (!propertyDesc) propertyDesc = @"<nil>";
                            } break;
                            case YYEncodingTypeSEL: {
                                SEL sel = ((SEL (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter);
                                if (sel) propertyDesc = NSStringFromSelector(sel);
                                else propertyDesc = @"<NULL>";
                            } break;
                            case YYEncodingTypeBlock: {
                                id block = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter);
                                propertyDesc = block ? ((NSObject *)block).description : @"<nil>";
                            } break;
                            case YYEncodingTypeCArray: case YYEncodingTypeCString: case YYEncodingTypePointer: {
                                void *pointer = ((void* (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter);
                                propertyDesc = [NSString stringWithFormat:@"%p",pointer];
                            } break;
                            case YYEncodingTypeStruct: case YYEncodingTypeUnion: {
                                NSValue *value = [model valueForKey:property->_name];
                                propertyDesc = value ? value.description : @"{unknown}";
                            } break;
                            default: propertyDesc = @"<unknown>";
                        }
                    }
                    
                    propertyDesc = ModelDescriptionAddIndent(propertyDesc.mutableCopy, 1);
                    [desc appendFormat:@"    %@ = %@",property->_name, propertyDesc];
                    [desc appendString:(i + 1 == max) ? @"
    " : @";
    "];
                }
                [desc appendFormat:@"}"];
                return desc;
            }
        }
    }

     

  • 相关阅读:
    vue 之 vuex
    vue中this.$router.push() 传参
    ES6新特性
    css优先级
    创建第一个vue工程
    对Vue.js的认知
    前端的认知与见解
    Web前端常见问题
    数据库如何进行索引优化
    Python FAQ
  • 原文地址:https://www.cnblogs.com/machao/p/5608260.html
Copyright © 2011-2022 走看看