YYKit的NSObject+YYModel学习



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






学习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 *)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 *)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 *)modelPropertyBlacklist;

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




5.+ (nullable NSArray *)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
@property (nonatomic, strong) NSDictionary *likedUsers; // Key:name(NSString) Value:user(YYUser)
@property (nonatomic, strong) NSSet *likedUserIds; // Set
@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 
@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学习)