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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值