KVC、KVO
1.KVC == KEY VALUE CODING
KVC其实就相当于setter和getter方法
Value : 值, 只能传对象
KVC一般用在两个地方:1)给私有变量赋值;2)字典转模型
在使用系统控件时,系统控件里面的私有属性我们无法设置,这是就可以用KVC
forKey: 需要给谁(哪个属性)赋值
setValue:forKey:方法, 只能给对象的直接属性赋值
setValue:forKeyPath: 可以给对象的间接属性赋值. 多层赋值
以后在开发中都使用setValue:forKeyPath:
valueForKey: 取值
2. 给私有成员变量赋值
Person.h文件
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property (nonatomic, copy)NSString *name;
@property (nonatomic, assign)double money;
@property (nonatomic, strong)Dog *dog;
@end
Person.h文件
#import "Person.h"
@implementation Person
{
@private
int _age;
{
@private
int _age;
}
- (void)say
{
NSLog(@"age = %i", _age);
}
{
NSLog(@"age = %i", _age);
}
@end
主头文件
int main() {
// 用KVC设置私有变量
Person *p = [Person new];
[p setValue:@"lnj" forKey:@"_name"];
[p setValue:@(30) forKey:@"_age"];
// 用SEL调用私有方法:
SEL sel = @selector(say);
// 用KVC设置私有变量
Person *p = [Person new];
[p setValue:@"lnj" forKey:@"_name"];
[p setValue:@(30) forKey:@"_age"];
// 用SEL调用私有方法:
SEL sel = @selector(say);
[p performSelector:sel];
// [p say];不能调私有方法
return 0;
}
[p setValue:@"lnj" forKey:@"_name"];
[p setValue:@(30) forKey:@"_age"];
用SEL来调用私有方法:
SEL sel = @selector(say);
[p performSelector:sel];
[p say];
所以OC并没有真正意义上的私有方法私有变量![]()
3.通过KVC赋值
#pragma mark 单个值
forKey: 需要给谁(哪个属性)赋值
setValue:forKey:方法, 只能给对象的直接属性赋值
[p setValue:@"lmj" forKey:@"name"];
@(998.0) == [NSNumber numberWithDouble:(double)]
[p setValue:@(668.0) forKey:@"money"];
setValue:forKey:方法, 只能给对象的直接属性赋值
[p setValue:@"lmj" forKey:@"name"];
@(998.0) == [NSNumber numberWithDouble:(double)]
[p setValue:@(668.0) forKey:@"money"];
#pragma mark 多层赋值
p.dog = [Dog new];
// p.dog.name == [[p dog] setName:]
p.dog.name = @"wangwang";
p.dog.price = 110.0;
setValue:forKeyPath: 可以给对象的间接属性赋值. 多层赋值
建议: 以后在开发中都使用setValue:forKeyPath:
[p setValue:@"xiaoqiang" forKeyPath:@"dog.name"];
setValue:forKeyPath: 可以给对象的间接属性赋值. 多层赋值
建议: 以后在开发中都使用setValue:forKeyPath:
[p setValue:@"xiaoqiang" forKeyPath:@"dog.name"];
[p setValue:@(110) forKeyPath:@"dog.price"];
4.通过KVC获取值
#pragma mark 获取单个值
NSString *name = [p valueForKey:@"name"];
NSLog(@"name = %@", name);
double money = [[p valueForKey:@"money"] doubleValue];
NSLog(@"name = %@", name);
double money = [[p valueForKey:@"money"] doubleValue];
NSLog(@"money = %f", money);
#pragma mark 获取多层值
NSString *dogName = [p valueForKey:@"dog.name"];
NSString *dogName = [p valueForKeyPath:@"dog.name"];
NSString *dogName = [p valueForKeyPath:@"dog.name"];
NSLog(@"dogName = %@", dogName);
#pragma mark 获取数组中对象的值
Person *p1 = [Person new];
p1.name = @"zs";
p1.money = 111;
Person *p2 = [Person new];
p2.name = @"ls";
p2.money = 222;
Person *p3 = [Person new];
p3.name = @"ww";
p3.money = 666;
p1.name = @"zs";
p1.money = 111;
Person *p2 = [Person new];
p2.name = @"ls";
p2.money = 222;
Person *p3 = [Person new];
p3.name = @"ww";
p3.money = 666;
NSArray *arr = @[p1, p2, p3];
// 如果数组中的元素都是同一种类型的数据, 可以使用KVC获取数组中所有对象的某个属性的值
NSArray *res = [arr valueForKeyPath:@"name"];
NSLog(@"res = %@", res);
#pragma mark 运算符
id res1 = [arr valueForKeyPath:@"@avg.money”]; // 获取数组中的平局值
NSLog(@"res = %@", res1);
5.字典转模型
1)如果想使用KVC进行字典转模型, 那么字典中的key必须和模型中的属性一模一样(个数 + 名称)
不对应会报以下错误
this class is not key value coding-compliant for the key score.'
2)如果使用KVC进行字典转模型, 只能对当前调用KVC方法的对象进行转换, 不能对它的属性的对象进行转换
setValuesForKeysWithDictionary:方法内部的实现原理
会拿到字典中的key, 然后根据这个key取出字典中的值, 然后再根据这个key赋值给对象。
模型转字典
例如:NSDictionary *dict = [p dictionaryWithValuesForKeys:@[@"name", @"money"]];
6、 KVO == Key Value Observing
作用: 可以监听某个对象属性的改变
KVO的原理:
只要给一个对象注册一个监听, 那么在运行时, 系统就会自动给该对象生成一个子类对象,
只要给一个对象注册一个监听, 那么在运行时, 系统就会自动给该对象生成一个子类对象,
并且重写自动生成的子类对象的被监听属性的set方法, 然后在set方法中通知监听者
NSKVONotifying_Person
Person *p = [Person new];
p.name = @"lnj";
p.name = @"lnj";
p.age = 30;
// 给p这个对象添加一个监听 , 监听p对象的age属性的改变, 只要age属性改变就通知self
[p addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];
第一个参数: 告诉系统哪个对象监听; 第二个参数: 监听当前对象的哪个属性; 第三个参数: 监听到属性改变之后, 传递什么值;第四个参数: 需要传递的参数 (这个参数不是传递给属性的)
p.age = 50;
// 从p对象上移除self对它的age属性的监听
[p removeObserver:self forKeyPath:@"age"];
// 只要监听到属性的改变就会调用
// keyPath: 被监听的属性名称 object : 被监听的对象 context: 注册监听的时候传入的值
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary *)change context:(nullable void *)context
{
NSLog(@"keyPath = %@, object = %@ , change = %@, context = %@", keyPath, object, change, context);
{
NSLog(@"keyPath = %@, object = %@ , change = %@, context = %@", keyPath, object, change, context);
}
注意: 如果使用KVO监听某个对象的属性, 当对象释放之前一定要移除监听
reason: 'An instance 0x7f9483516610 of class Person was deallocated while key value observers were still registered with it.
reason: 'An instance 0x7f9483516610 of class Person was deallocated while key value observers were still registered with it.
注意: KVO只能监听通过set方法修改的值