博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
YYModel 源码解读(二)之NSObject+YYModel.h (4)
阅读量:5272 次
发布时间:2019-06-14

本文共 18883 字,大约阅读时间需要 62 分钟。

接下来我们继续向下看

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、NSNullNSDictionary 对象key 必须是 NSStringvalue只能是 NSString、NSNumber、NSNull该函数对传入的NSObject对象为如下所有类型时做的预处理:NSData >>> 不能转换成JSONNSString >>> NSStringNSNumber >>> NSNumberNSURL >>> NSStringNSAttributedString >>> NSStringNSDate >>> 使用DateFormat转换成NSStringNSArray先判断NSArray是否可以被JSON化如果可以,直接返回NSArray如果不可以创建一个新的NSArray遍历之前NSArray中的每一个元素递归将当前元素解析成 NSString、NSNumber将解析后的元素添加到数组返回新的数组NSSetNSSet >>> 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
)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 stringstatic NSString *ModelDescription(NSObject *model) {        // 1. 设置描述的最大长度为100    static const int kDescMaxLength = 100;        // 2. 进行判空或者不是NSObject的处理    if (!model) return @"
"; if (model == (id)kCFNull) return @"
"; 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 = @"
"; } break; case YYEncodingTypeClass: { id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter); propertyDesc = ((NSObject *)v).description; if (!propertyDesc) propertyDesc = @"
"; } break; case YYEncodingTypeSEL: { SEL sel = ((SEL (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter); if (sel) propertyDesc = NSStringFromSelector(sel); else propertyDesc = @"
"; } break; case YYEncodingTypeBlock: { id block = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter); propertyDesc = block ? ((NSObject *)block).description : @"
"; } 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 = @"
"; } } propertyDesc = ModelDescriptionAddIndent(propertyDesc.mutableCopy, 1); [desc appendFormat:@" %@ = %@",property->_name, propertyDesc]; [desc appendString:(i + 1 == max) ? @"\n" : @";\n"]; } [desc appendFormat:@"}"]; return desc; } }}

 

 

转载于:https://www.cnblogs.com/machao/p/5608260.html

你可能感兴趣的文章
【hdu 1429】胜利大逃亡(续)
查看>>
Factory Design Pattern
查看>>
P1192-台阶问题
查看>>
Java大数——a^b + b^a
查看>>
简单的数据库操作
查看>>
帧的最小长度 CSMA/CD
查看>>
树状数组及其他特别简单的扩展
查看>>
普通求素数和线性筛素数
查看>>
PHP截取中英文混合字符
查看>>
电子眼抓拍大解密
查看>>
51nod1076 (边双连通)
查看>>
Linux pipe函数
查看>>
java equals 小记
查看>>
2019春 软件工程实践 助教总结
查看>>
Zerver是一个C#开发的Nginx+PHP+Mysql+memcached+redis绿色集成开发环境
查看>>
程序的静态链接,动态链接和装载 (补充)
查看>>
关于本博客说明
查看>>
[Kaggle] Sentiment Analysis on Movie Reviews
查看>>
价值观
查看>>
mongodb命令----批量更改文档字段名
查看>>