iOS KVC-对象初始化赋值的神级技巧

来源:互联网 发布:淘宝淘客怎么设置 编辑:程序博客网 时间:2024/05/16 17:55

一直没想过会写一篇这样的文章,在进入职业生涯的第二家公司,前后看了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了,是不是很爽?
夜深人静,天明还要上班,写得比较捉急,不明白的地方欢迎留言讨论,该方法如有不妥或不足之处,欢迎指正。

原创粉丝点击