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\nxxxxxxxxxxxxxxxxxxxxxxxx\nxxxxxxxxxxxxxxxxxxxxxxxx";

显示是这样的

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 == '\n') {
            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:@"[\n"];
                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) ? @"\n" : @";\n"];
                }
                [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:@"{\n"];
                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) ? @"\n" : @";\n"];
                }
                [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:@" {\n"];
            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) ? @"\n" : @";\n"];
            }
            [desc appendFormat:@"}"];
            return desc;
        }
    }
}

 

 

posted @ 2016-06-22 19:18  马在路上  阅读(1126)  评论(0编辑  收藏  举报