KVC/KVO入门

前言

KVC/KVO在iOS设计模式中具有很重要的地位,在平常的优化代码,面试问答等经常会用到,甚至很多高级的iOS开发技巧都是基于KVC实现的,因此,想提高自己的开发能力,不能仅限于掌握一些KVC/KVO 的基础用法,必须对期底层原理知其所以然,逐步的推敲和掌握,应用到自己的代码中。目前网上关于KVC/KVO的文章在非常多,小编打算站在巨人的肩膀上,加上自己的一些开发经验和理解,遵循由浅到深的原则,通过一系列文章对KVC/KVO 进行整理和归纳,本文先整理了一些KVC/KVO的简单用法。


KVC定义

KVC就是key-value-coding(键值编码),简而言之就是通过key值去操作对象的属性,进行赋值和取值。


KVC最大的优点

通过Key值直接访问对象的属性,进行赋值和取值。而不需要调用明确的存取方法。这样就可以在运行时动态在访问和修改对象的属性。而不是在编译时确定,可以算得上是iOS开发中的黑魔法之一


KVC最常用的几个函数(我归纳为四大金刚)

setValue:forKey:(为对象的属性赋值)
setValue:forKeyPath:(包含了setValue:forKey:的功能,还可以对对象内的类的属性进行赋值)
valueForKey:(根据key取值)
valueForKeyPath:(根据keyPath取值)

四大金刚使用举例 (生成一个 Person类 和 Car 类)

person.h

@class Car;
@interface Persion : NSObject
{
    NSString *_adress;
}
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) Car *car;
@end

Car.h

@interface Car : NSObject
@property (nonatomic, strong) NSNumber *price;
@end

ViewController.m

扫描二维码关注公众号,回复: 13259861 查看本文章
- (void)viewDidLoad {
    [super viewDidLoad];
    Persion *persion = [[Persion alloc] init];
    [persion setValue:@"zyy1" forKey:@"name"];
    [persion setValue:@"zyy2" forKeyPath:@"name"];
    NSLog(@"persion.name===%@",persion.name);

    Car *car = [[Car alloc] init];
    persion.car = car;
    [persion setValue:@500 forKeyPath:@"car.price"];
    NSLog(@"car.price====%@",[persion valueForKeyPath:@"car.price"] );

    [persion setValue:@"北京大望路1" forKeyPath:@"adress"];
    [persion setValue:@"北京大望路2" forKeyPath:@"_adress"];
    NSLog(@"adress===%@", [persion valueForKey:@"adress"]);
    NSLog(@"_adress===%@",[persion valueForKey:@"_adress"]);
}

输出结果

2016-10-27 15:00:01.989 KVC[5749:227850] persion.name===zyy2
2016-10-27 15:00:01.989 KVC[5749:227850] car.price====500
2016-10-27 15:00:01.990 KVC[5749:227850] adress===北京大望路2
2016-10-27 15:00:01.990 KVC[5749:227850] _adress===北京大望路2

通过以上代码执行的结果,我们进行分析比较下面几个问题

KVC中使用forKeyPath:

一个类的成员变量有可能是其他的自定义类,你可以先用KVC获取全部属性,然后再次用KVC来获取这个自定义类的属性,但这样是比较繁琐的,对此,KVC提供了一个解决方案,那就是键路径KeyPath


KVC中 forKey: 和 forKeyPath: 区别

对一个对象的一般属性进行赋值、取值,两个方法是通用,对对象中的对象进的属性行赋值,只有keyPath能够实现


KVC和对象的setter、getter方法的区别

一般情况下,KVC和setter、getter应该说都能达到对对象属性的赋值,并且KVC操作本质也是去调用的setter方法和getter方法,但是对于一些私有属性,那么这个时候setter、getter方法就没有用了,这个时候KVC却可以


KVC中其他几个函数举例

// 默认返回YES,表示如果没有找到Set方法的话,会按照_key,_iskey的顺序搜索成员,设置成NO就不这样搜索
+ (BOOL)accessInstanceVariablesDirectly {
    return YES;
}
/**
 在类的内部,进行检查,不符合要求 返回NO ,类外部可以使用

 @param ioValue  比对赋值类型
 @param inKey    比对属性
 @param outError 报错信息

 @return 先判断是否符合要求,再使用KVC
 */
- (BOOL)validateValue:(inout id  _Nullable __autoreleasing *)ioValue forKey:(NSString *)inKey error:(out NSError * _Nullable __autoreleasing *)outError {
    if ([*ioValue isKindOfClass:[NSString class]] && [inKey isEqualToString:@"name"]) {
        return YES;
    }
    return NO;
}
//如果Key不存在,且KVC无法搜索到任何和Key有关的字段或者属性,则会调用这个方法,默认是抛出异常
- (nullable id)valueForUndefinedKey:(NSString *)key {

}
//和上一个方法一样,只不过是设值
- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key {

}
//如果你在SetValue方法时面给Value传nil,则会调用这个方法
- (void)setNilValueForKey:(NSString *)key {

}
//输入一组key,返回该组key对应的Value,再转成字典返回,用于将Model转到字典。
- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys {

}
//这是集合操作的API,里面还有一系列这样的API,如果属性是一个NSMutableArray,那么可以用这个方法来返回
- (NSMutableSet *)mutableSetValueForKey:(NSString *)key {

}
//字典转模型
- (instancetype)initWithDict:(NSDictionary *)dict {
    if (self = [super init]) {
        [self setValuesForKeysWithDictionary:dict];
    }
    return self;
}
注意点: 
- 字典转模型的时候,字典中的某一个key一定要在模型中有对应的属性 
- 如果一个模型中包含了另外的模型对象,是不能直接转化成功的。 
- 通过kvc转化模型中的模型,也是不能直接转化成功的。

上面的这些方法在碰到特殊情况或者有特殊需求还是会用到的,所以可以了解一下.


KVO

KVO也就是key-value-observing(即键值观察),利用一个key来找到某个属性并监听其值得改变。

用法如下:

1、添加观察者
2、在观察者中实现监听方法,observeValueForKeyPath: ofObject: change: context:
3、移除观察者 -

// 让self去观察persion对象的name属性 (self为观察者、监听者, persion为被观察者、被监听者)
    [persion addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
    persion.name = @"zyy3";
}


/**
 当被监听的对象的属性值发生改变时,观察者会调用该方法
 @param keyPath 监听的属性
 @param object  监听的对象
 @param change  新旧值
 @param context 额外参数
*/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    NSLog(@"%@发生改变",keyPath);
    NSLog(@"change==%@",change);
}

// 移除observer
- (void)dealloc {
    [persion removeObserver:self forKeyPath:@"name"];
}

以上就是一些 KVC/KVO的一些基础用法。

猜你喜欢

转载自blog.csdn.net/u014641631/article/details/52956633