美文网首页理论iOS 开发基础
YYKit的NSObject+YYModel学习

YYKit的NSObject+YYModel学习

作者: T_aa | 来源:发表于2016-07-04 14:11 被阅读1821次

    项目地址点这里......





    学习NSObject+YYModel之前要先了学一下他YYClassInfo类.

    如果runtime有了解过,就会发现yyclassinfo 是对ivar,method,objc_property,class这三个做了整理归纳定义,对应YYClassIvarInfo,YYClassMethodInfo,YYClassPropertyInfo,YYClassInfo更容易看懂他们每个类所拥有的特性,也更方便的直接的使用。他们的实现也是基于Runtime,具体可以看YYKit的源码.

    参考:http://www.jianshu.com/p/36273f598731

    开始学习NSObject+YYModel

    #define force_inline __inline__ __attribute__((always_inline)) 定义内联函数

    获取这个属性的类型信息
    static force_inline YYEncodingNSType YYClassGetNSType(Class cls){}
    
    是否是number型,基本类型
    static force_inline BOOL YYEncodingTypeIsCNumber(YYEncodingType type)
    
    从id中解析nsnumber类型 不是很懂
    static force_inline NSNumber *YYNSNumberCreateFromID(__unsafe_unretained id value){}
    
    字符串转化为date类型
    static force_inline NSDate *YYNSDateFromString(__unsafe_unretained NSString *string) {}
    
    定义NSBlock类
    static force_inline Class YYNSBlockClass(){}
    
    获取国际标准化组织的日期格式
    static force_inline NSDateFormatter *YYISODateFormatter() {}
    
    获取字典里面一序列键值对应的值
    static force_inline id YYValueForKeyPath(__unsafe_unretained NSDictionary *dic, __unsafe_unretained NSArray *keyPaths) {}
    
    获取字典里面一序列键值对应的值 感觉跟上面一个方法没什么差
    static force_inline id YYValueForMultiKeys(__unsafe_unretained NSDictionary *dic, __unsafe_unretained NSArray *multiKeys) {}
    
    通过属性获取number
    static force_inline NSNumber *ModelCreateNumberFromProperty(__unsafe_unretained id model,                                             __unsafe_unretained _YYModelPropertyMeta *meta) {}
    
    设置number到属性
    static force_inline void ModelSetNumberToProperty(__unsafe_unretained id model,
                                                      __unsafe_unretained NSNumber *num,
                                                      __unsafe_unretained _YYModelPropertyMeta *meta) {}
                                                                                                  
     
    设置值与属性的元模型。                                               
    static void ModelSetValueForProperty(__unsafe_unretained id model,
                                         __unsafe_unretained id value,
                                         __unsafe_unretained _YYModelPropertyMeta *meta) {}
          
          
    应用函数字典,设置键-值对模型。   
    static void ModelSetWithDictionaryFunction(const void *_key, const void *_value, void *_context) {}
    
    应用属性元函数模型,设置字典模型。
    static void ModelSetWithPropertyMetaArrayFunction(const void *_propertyMeta, void *_context) {}     
    
    将模型转化成json串
    static id ModelToJSONObjectRecursive(NSObject *model) {}                                       
    
    增加缩进字符串(排除第一行)
    static NSMutableString *ModelDescriptionAddIndent(NSMutableString *desc, NSUInteger indent) {}
    
    模型生成一个字符串描述
    static NSString *ModelDescription(NSObject *model) {}
    

    NSObject+YYModel


    1.+ (NSDictionary *)_yy_dictionaryWithJSON:(id)json {}
    将传进来的json类型转化为字典



    2.+ (instancetype)modelWithJSON:(id)json {}
    通过传进来的json实例该类对象,通过 modelWithDictionary转化



    3.+ (instancetype)modelWithDictionary:(NSDictionary *)dictionary {}

    • 通过私有类_YYModelMeta(模型元件)将该类中的属性,变量,方法,映射信息都提取出来 在其中_YYModelMeta中:
    类的基本信息
    YYClassInfo *_classInfo; 
    
    属性信息和_YYModelPropertyMeta属性类的映射
    NSDictionary *_mapper; 
    
    所有属性对象的数组,每个元素是_YYModelPropertyMeta类型
    NSArray *_allPropertyMetas;
    
    所有属性对应的keypath的映射的数组,每个属性类型也都是_YYModelPropertyMeta类型
    NSArray *_keyPathPropertyMetas;
    
    有多个key映射
    NSArray *_multiKeysPropertyMetas;
    
    映射的数量
    NSUInteger _keyMappedCount;
    
    类编码类型
    YYEncodingNSType _nsType;
    
    是否执行 modelCustomWillTransformFromDictionary 自定义对象将要转化为模型的时候,如果有实现该方法,他将先于 `+modelWithJSON:`, `+modelWithDictionary:`, `-modelSetWithJSON:` 和 `-modelSetWithDictionary:`这些方法执行,返回是一个字典类型.
    BOOL _hasCustomWillTransformFromDictionary;
    
    是否执行 modelCustomTransformFromDictionary, 返回的是一个布尔值,如果是默认的json-to-model转化的时候某个字段适合你的自定义的模型,可以去实现这个额外的方法,你也可以使用这个方法来校验你属性的类型,返回YES这个模型是可用的,NO则不可用,具体看例子实现
    BOOL _hasCustomTransformFromDictionary;
    
    是否执行 modelCustomTransformToDictionary,如果是默认的json-to-model转化的时候某个字段适合你的自定义的模型,返回YES这个模型是可用的,NO则不可用,具体看例子实现
    BOOL _hasCustomTransformToDictionary;
    
    是否执行 modelCustomClassForDictionary,在json-to-model转化的时候,通过该方法去实例不同的类类型
    BOOL _hasCustomClassFromDictionary;
    
    

    在YYModeleMeta中的司机BOOL类型的赋值通过instancesRespondToSelectorrespondsToSelector来做判断是否实现对应的方法

    他们的区别参考:

    http://www.jianshu.com/p/ae494b5eeb86
    http://www.jianshu.com/p/c37f5d97b07e
    http://blog.csdn.net/yxwlzsh/article/details/49755823

    instancesRespondToSelector是类方法,用于判断实例后的对象是否绑定某个方法,只能用来判断实例方法。作用于类

    类名 + 实例方法

    respondsToSelector是实例方法,用于判断实例后的对象是否绑定某个方法,和类是否实现某个类方法,作用于对象和类

    类名 + 类方法

    实例之后的对象 + 实例方法

    所有在上面前三个BOOL是直接通过类名来判断实例方法所以使用instancesRespondToSelector,最后一个是直接判断类方法所以使用respondsToSelector

    回到YYKit的model

    • 通过_hasCustomClassFromDictionary判断是否有自定义类,YES则执行modelCustomClassForDictionary

    modelCustomClassForDictionary是用户自己在外部实现的方法,传递自己要实例的类类型。

    • 通过确定的class类new一个对象,通过modelSetWithDictionary方法实现dic-to-model



    4.- (BOOL)modelSetWithJSON:(id)json {}

    判断这个json是否可转化成对象



    5.- (BOOL)modelSetWithDictionary:(NSDictionary *)dic {}

    实现Dictionary-To-Model

    1. 通过该类实例_YYModeMeta类型
    2. 如果没有键映射的count是0则不能实例,返回空,如果是集成NSObject的模型的话至少有一个isa的地址的键值映射,一般都会有
    3. 判断是否_hasCustomWillTransformFromDictionary值,YES则执行modelCustomWillTransformFromDictionary
    4. 定义结构体ModelSetContext,其中modelMeta存_YYModelMeta实例的model,model该类实例的对象,dictionary要转化的字典类型
    5. 给模型赋值的过程用到了CoreFoundation相关知识,不是很清楚,后面学习一下,先跳过。
    6. 转化完成之后,_hasCustomTransformFromDictionary判断是否要自动已转化从字典中,YES执行modelCustomTransformFromDictionary

    6.- (id)modelToJSONObject {}将模型转化为NSArray或者NSDictionary类型,其中里面所有的对象都是通过 NSString, NSNumber, NSArray, NSDictionary, or NSNull.实例的,可直接转化为json数据

    7.- (NSData *)modelToJSONData {}将对象转化成Data数据

    8.- (NSString *)modelToJSONString {}将对象转化成json字符串

    9.- (id)modelCopy{}拷贝复制对象

    10.- (void)modelEncodeWithCoder:(NSCoder *)aCoder {}- (id)modelInitWithCoder:(NSCoder *)aDecoder {}实现NSCoding编码解码,使用的时候:

    - (void)encodeWithCoder:(NSCoder *)aCoder
    {
     [self modelEncodeWithCoder:aCoder]; 
     }
    - (id)initWithCoder:(NSCoder *)aDecoder 
    {
     return [self modelInitWithCoder:aDecoder]; 
    }
    

    一句编码解码

    11.- (NSUInteger)modelHash {}model哈希

    12.- (BOOL)modelIsEqual:(id)model {}两个model是否一样,
    如果地址指针是否一样或者里面的所有的属性信息值都是否一样

    13.- (NSString *)modelDescription {}该类的字符串描述

    例子(具体可以看YYKit里面的YYModelExample):



    NSArray+YYModel


    + (NSArray *)modelArrayWithClass:(Class)cls json:(id)json {}
    如果json串可转化成数组类型,将这个json数组中的每个元素转化成cls类型元素,重新转成变成数组返回,数组中的元素都是cls类型。



    NSDictionary+YYModel

    + (NSDictionary *)modelDictionaryWithClass:(Class)cls json:(id)json {}
    跟数组的YYModel一样,key对应的value转化成cls类型value


    @protocol YYModel


    该协议不需要去集成添加,是为了方法的扩展。为了转模型的时候更好的定义。

    1.+ (nullable NSDictionary<NSString *, id> *)modelCustomPropertyMapper;

    自定义属性的映射。如果JSON或者Dic的key在model中找不到对应的关联属性,可以用这个方法添加映射.

    Example:

    json: 
            {
                "n":"Harry Pottery",
                "p": 256,
                "ext" : {
                    "desc" : "A book written by J.K.Rowling."
                },
                "ID" : 100010
            }
     
        model:
            @interface YYBook : NSObject
            @property NSString *name;
            @property NSInteger page;
            @property NSString *desc;
            @property NSString *bookID;
            @end
            
            @implementation YYBook
            + (NSDictionary *)modelCustomPropertyMapper {
                return @{@"name"  : @"n",
                         @"page"  : @"p",
                         @"desc"  : @"ext.desc",
                         @"bookID": @[@"id", @"ID", @"book_id"]};
            }
            @end
    

    属性name的值对应的是Json中的n的key的键值,

    属性page的值对应的是Json中的p的key的键值,

    属性desc的值对应的是Json中的ext.desc的key的键值,其中ext.desc是路径的key,多层的话 ext.s1.s2.s3...

    属性bookID的值对应的是Json中的idID,book_id的key的键值,多个key的值都是对应的bookID的属性,可以用数组的方法传值。很贴心啊。



    2.+ (nullable NSDictionary<NSString *, id> *)modelContainerPropertyGenericClass;

    属性内包含的对象自定义,如果一个属性是一个容器的对象,比如是NSArray/NSSet/NSDictionary,实现这个方法返回对应属性的映射,知道哪一个类将被添加到容器中,容器中的对象是哪一种类类型。

    Example:

    @class YYShadow, YYBorder, YYAttachment;
     
    @interface YYAttributes
    @property NSString *name;
    @property NSArray *shadows;
    @property NSSet *borders;
    @property NSDictionary *attachments;
    @end
    
    @implementation YYAttributes
    + (NSDictionary *)modelContainerPropertyGenericClass {
    return @{@"shadows" : [YYShadow class],
         @"borders" : YYBorder.class,
         @"attachments" : @"YYAttachment" };
    }
    @end
    

    其中这个类中有三个是容器的属性类型。shadows中是YYShadow元素类型,borders是YYBorder元素类型,NSDictionary是YYAttachment元素类型。自定义类型写法的时候可以有这个三种写法:[YYShadow class],YYBorder.class,@"YYAttachment".



    3.+ (nullable Class)modelCustomClassForDictionary:(NSDictionary *)dictionary;

    上面讲过,根据dic来实例不通的类类型

    Example:

    @class YYCircle, YYRectangle, YYLine;
     
    @implementation YYShape
    
    + (Class)modelCustomClassForDictionary:(NSDictionary*)dictionary {
        if (dictionary[@"radius"] != nil) {
            return [YYCircle class];
        } else if (dictionary[@"width"] != nil) {
            return [YYRectangle class];
        } else if (dictionary[@"y2"] != nil) {
            return [YYLine class];
        } else {
            return [self class];
        }
    }
    @end
    



    4.+ (nullable NSArray<NSString *> *)modelPropertyBlacklist;

    哪些属性在模型转化的过程中将被忽略,黑名单



    5.+ (nullable NSArray<NSString *> *)modelPropertyWhitelist;

    不在这个属性列表中的属性模型转化过程中将被忽略,白名单






    例子

    • 普通对象实例
    YHBook * book = [YHBook modelWithJSON:@"     \
                         {                                           \
                         \"name\": \"Harry Potter\",              \
                         \"pages\": 512,                          \
                         \"publishDate\": \"2010-01-01\"          \
                         }"];
        
        NSLog(@"name = %@__date = %@",book.name,book.publishDate);
        
        NSLog(@"BOOk : %@",[book modelToJSONString]);
    

    如果是直接使用modewithJson的时候没有实现其他的自定义方法的时候YHBook中定义的属性的名字需和json串中每个字段对应的key的名字一样。

    • 内嵌对象实例
    YYRepo * repo = [YYRepo modelWithJSON:@"         \
                         {                                               \
                         \"rid\": 123456789,                         \
                         \"name\": \"YYKit\",                        \
                         \"createTime\" : \"2011-06-09T06:24:26Z\",  \
                         \"owner\": {                                \
                            \"uid\" : 989898,                       \
                            \"name\" : \"ibireme\"                  \
                         } \
                         }"];
        
        NSLog(@"owner name %@",repo.owner.name);
        
        NSLog(@"Repo: %@", [repo modelToJSONString]);
    

    json串中在ownerkey这边有一个新的一级字典数据,外面一层通过YYRepo来实例,owner这边通过YYUser来实例。

    • 有属性是容器类型的属性,定义该属性中元素类类型的实例
    
    @interface YYPhoto : NSObject
    @property (nonatomic, copy) NSString *url;
    @property (nonatomic, copy) NSString *desc;
    @end
    
    @implementation YYPhoto
    @end
    
    @interface YYAlbum : NSObject
    @property (nonatomic, copy) NSString *name;
    @property (nonatomic, strong) NSArray *photos; // Array<YYPhoto>
    @property (nonatomic, strong) NSDictionary *likedUsers; // Key:name(NSString) Value:user(YYUser)
    @property (nonatomic, strong) NSSet *likedUserIds; // Set<NSNumber>
    @end
    
    @implementation YYAlbum
    + (NSDictionary *)modelContainerPropertyGenericClass {
        return @{@"photos" : YYPhoto.class,
                 @"likedUsers" : YYUser.class,
                 @"likedUserIds" : NSNumber.class};
    }
    @end
    
    static void ContainerObjectExample() {
        YYAlbum *album = [YYAlbum modelWithJSON:@"          \
        {                                                   \
        \"name\" : \"Happy Birthday\",                      \
        \"photos\" : [                                      \
            {                                               \
                \"url\":\"http://example.com/1.png\",       \
                \"desc\":\"Happy~\"                         \
            },                                              \
            {                                               \
                \"url\":\"http://example.com/2.png\",       \
                \"desc\":\"Yeah!\"                          \
            }                                               \
        ],                                                  \
        \"likedUsers\" : {                                  \
            \"Jony\" : {\"uid\":10001,\"name\":\"Jony\"},   \
            \"Anna\" : {\"uid\":10002,\"name\":\"Anna\"}    \
        },                                                  \
        \"likedUserIds\" : [10001,10002]                    \
        }"];
        NSString *albumJSON = [album modelToJSONString];
        NSLog(@"Album: %@", albumJSON);
    }
    
    

    通过+ (NSDictionary *)modelContainerPropertyGenericClass{}来定义photos、photos、likedUserIds这三个属性中其元素的类类型.

    • NSCoding编码、解码, NSCopying拷贝,哈希转化
    
    @interface YYShadow :NSObject <NSCoding, NSCopying>
    @property (nonatomic, copy) NSString *name;
    @property (nonatomic, assign) CGSize size;
    @property (nonatomic, strong) UIColor *color;
    @end
    
    @implementation YYShadow
    - (void)encodeWithCoder:(NSCoder *)aCoder { [self modelEncodeWithCoder:aCoder]; }
    - (id)initWithCoder:(NSCoder *)aDecoder { return [self modelInitWithCoder:aDecoder]; }
    - (id)copyWithZone:(NSZone *)zone { return [self modelCopy]; }
    - (NSUInteger)hash { return [self modelHash]; }
    - (BOOL)isEqual:(id)object { return [self modelIsEqual:object]; }
    @end
    
    static void CodingCopyingHashEqualExample() {
        YYShadow *shadow = [YYShadow new];
        shadow.name = @"Test";
        shadow.size = CGSizeMake(10, 0);
        shadow.color = [UIColor blueColor];
        
        YYShadow *shadow2 = [shadow deepCopy]; // Archive and Unachive
        //    shadow2.name = @"TT";
        BOOL equal = [shadow isEqual:shadow2];
        NSLog(@"shadow equals: %@",equal ? @"YES" : @"NO");
    }
    
    
    

    NSObject+YYModel 的model大家多试试

    有意见或者错误欢迎提出...

    共进...

    相关文章

      网友评论

        本文标题:YYKit的NSObject+YYModel学习

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