项目地址点这里......
学习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类型的赋值通过instancesRespondToSelector
和respondsToSelector
来做判断是否实现对应的方法
他们的区别参考:
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
- 通过该类实例_YYModeMeta类型
- 如果没有键映射的count是0则不能实例,返回空,如果是集成NSObject的模型的话至少有一个isa的地址的键值映射,一般都会有
- 判断是否
_hasCustomWillTransformFromDictionary
值,YES则执行modelCustomWillTransformFromDictionary - 定义结构体ModelSetContext,其中modelMeta存_YYModelMeta实例的model,model该类实例的对象,dictionary要转化的字典类型
- 给模型赋值的过程用到了CoreFoundation相关知识,不是很清楚,后面学习一下,先跳过。
- 转化完成之后,_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
自定义属性的映射。如果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中的id
、ID
,book_id
的key的键值,多个key的值都是对应的bookID的属性,可以用数组的方法传值。很贴心啊。
2.+ (nullable NSDictionary
属性内包含的对象自定义,如果一个属性是一个容器的对象,比如是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
哪些属性在模型转化的过程中将被忽略,黑名单
5.+ (nullable NSArray
不在这个属性列表中的属性模型转化过程中将被忽略,白名单
例子
- 普通对象实例
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大家多试试
有意见或者错误欢迎提出...
共进...