一直没想过会写一篇这样的文章,在进入职业生涯的第二家公司,前后看了9个同事(包括一些经验十分丰富的)的代码之后,决定还是写一下吧。
网络请求之后最重要的一步就是把字典数据赋值给类对像了,这一步很简单,但通常也异常繁琐,为了使程序健壮,最常见的写法是判断字典中是否存在这个key对应的value,如果存在则赋值,不存在则跳过或进行其他处理。
此篇文章将给出一个比较简单的初始化类对象的技巧,应用场景是最常见的通过字典数据给对像赋值。
原理是通过KVC提供的setValuesForKeysWithDictionary:方法达到快速赋值的目的,代码简洁,灵活且不易出错。
demo如下:
//POIInfor.h
@interface POIInfor : NSObject
@property(nonatomic, assign)NSInteger userId;
@property(nonatomic, assign)double createTime;
@property(nonatomic, copy) NSString * summary;
@property(nonatomic, copy) NSString * content;
@property(nonatomic, strong)NSNumber * inforId;
@property(nonatomic, strong)NSNumber * likeNum;
@property(nonatomic, strong)NSNumber * readNum;
@property(nonatomic, strong)NSArray * photos;//每个元素都是POIPhoto类型
@property(nonatomic, strong)NSNumber * isEditPhoto;
/**
通过字典创建并初始化一个类对像
题外话:
alt+command+/可给方法/属性/类添加注释,按住alt点击方法名即会出现注释内容,类似系统API注释,建议以此方法添加注释
*/
+ (instancetype)infoWithDictionary:(NSDictionary *)dict;
- (instancetype)initWithDictionary:(NSDictionary *)dict;
@end
//POIInfor.m
@implementation POIInfor
+ (instancetype)infoWithDictionary:(NSDictionary *)dict
{
return [[POIInfor alloc] initWithDictionary:dict];
}
- (instancetype)initWithDictionary:(NSDictionary *)dict
{
if (self = [super init]) {
//通过KVC方法可快速通过字典给对像赋值,将字典中的value赋值给与其key同名的property[基本数据类型(比如int等)无需做特殊处理]
[self setValuesForKeysWithDictionary:dict];
}
return self;
}
//若无需要特殊处理的key,此方法可省略
- (void)setValue:(id)value forKey:(NSString *)key
{
if ([key isEqualToString:@"id"]) {
//因为id是一个特殊的OC数据类型,属于系统保留字,因此不建议做为property直接使用
//此处将id转换为userId,不仅符合编码规范,而且达到了简明知意的作用
key = @"userId";
}
else if ([key isEqualToString:@"photos"]){
//遍历value数组,按照和POIInfor相同的方式创建并初始化POIPhoto对像
__block NSMutableArray * photos = [@[] mutableCopy];
if ([value isKindOfClass:[NSArray class]]) {
//遍历数组,个人比较喜欢用枚举,没别的,因为效率比for更高
//而且逆序遍历可变数组的时候可以对数组元素进行删除操作
[value enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj isKindOfClass:[NSDictionary class]]){
//POIPhoto没有写便利构造器,下面按步就班的初始化并赋值
POIPhoto * photo = [POIPhoto new];
[photo setValuesForKeysWithDictionary:obj];
[photos addObject:photo];
}
}];
}
value = photos;
}
//调用setValue:forKey:会将value赋给与key同名的property
//如果找不到与key同名的property,将会自动调用setValue:forUndefinedKey:寻求处理
//如果未实现setValue:forUndefinedKey:方法,将直接导致程序崩溃
//此方法可以将NSNumber对象的value转成对应的基本数据类型
[super setValue:value forKey:key];
}
- (void)setValue:(id)value forUndefinedKey:(NSString *)key
{
NSLog(@" -- undefined key : %@ -- ",key);
//为保持程序健壮,使用setValuesForKeysWithDictionary:方法给类赋值时必须重写此方法,一般空实现即可
}
@end
总结 & 注意事项:
1. 对象可通过setValuesForKeysWithDictionary:方法赋值
2. 若有需要特殊处理的property(key和property名称不对应/property是其他的自定义类对象),则需要实现setValue: forKey:方法,并在此方法中处理对应的情况;若没有需要特殊处理的property,则setValue:forKey方法可省略
3. 为保持程序的健壮性,建议实现setValue: forUndefinedKey:方法,一般空实现即可
4. 自定义类时,建议使用与字典同名的key做为property,并使用与value相同的数据类型(包括基本数据类型)
从此以后便可丢弃一大串的if-else了,是不是很爽?
夜深人静,天明还要上班,写得比较捉急,不明白的地方欢迎留言讨论,该方法如有不妥或不足之处,欢迎指正。