KVO和KVC可以说是好几年的技术了,之前一直用通知、代理以及Block,发现这几个已经够用了,就没有去仔细的去研究。当然了开发过程中也偶尔用到,那是在遇到困难问题的时候,在网上找的方法里面带有的,在第三方库里面也经常也到,今天有时间就稍微研究下,其实非常简单的,很容易入手应用。
一、KVC KVO
KVC(Key Value Coding)键值编码,是可以通过对象属性名称(Key)直接给属性值(Value)编码(Coding),这里的“编码”可以理解为“赋值”。这样可以免去我们调用getter和setter方法,从而简化我们的代码。也可以用来修改系统控件内部属性。
KVO : (Key Value Observer) 键值观察者,是观察者设计模式的一种具体实现(C层和M层的通信)。KVO触发机制:一个对象(观察者),检测另一个对象(被观察者)的某属性是否发生变化,若被监测的属性发生了更改,会触发观察者的一个方法(方法名固定,类似代理方法)
类:Teacher
.h
@interface Teacher : NSObject
@property (nonatomic , copy) NSString * name; ///< 姓名
@property (nonatomic , strong) Student *student; ///< 学生
@end
.m
@interface Teacher ()
{
NSString * _height;
}
@property (nonatomic , copy) NSString *age;
@end
类Student
.h
@interface Student : NSObject
@property (nonatomic , copy) NSString * weight; ///< 学生体重
@end
.m
@implementation Student
@end
类ViewController
.m
@interface ViewController ()
@property (nonatomic , strong) Teacher *teacher;
@end
- (void)viewDidLoad {
[super viewDidLoad];
self.teacher = [[Teacher alloc]init];
// 初始化Student
self.teacher.student = [[Student alloc]init];
// KVC赋值
// Teacher的公有属性
[self.teacher setValue:@"jack" forKey:@"name"];
// Teacher的私有属性也可以修改
[self.teacher setValue:@"18岁" forKey:@"age"];
[self.teacher.student setValue:@"30Kg" forKey:@"weight"];
//(对于height,KVC先从内存中寻找对应名为height的属性,如果找不到就会自动寻找_height,然后进行相应的修改)
[self.teacher setValue:@"183cm" forKeyPath:@"_height"];
[self.teacher setValue:@"193cm" forKeyPath:@"height"];
// 3.forKeyPath
// forKeyPath包含了forKey的功能,以后使用forKeyPath就可以了。
// forKeyPath可以利用‘ . ’运算符一层一层往下查找对象的属性
[self.teacher setValue:@"20" forKeyPath:@"age"];
[self.teacher setValue:@"30Kg" forKeyPath:@"student.weight"];
// KVC取值
NSLog(@"老师的身高:%@",[self.teacher valueForKeyPath:@"height"]);
NSLog(@"学生的体重:%@",[self.teacher valueForKeyPath:@"student.weight"]);
// NSLog(@"学生的体重:%@",[self.teacher.student valueForKey:@"weight"]);
// KVO 监测student的weight变化
// 注册观察者
// 1.observer: 观察者 (观察self.view对象的属性的变化)
// 2.KeyPath: 被观察属性的名称
// 3.options: 新值,旧值
// 4.context: 上下文 可以为kvo的回调方法传值
[self.teacher addObserver:self forKeyPath:@"student.weight" options:(NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld) context:nil];
}
#pragma mark - KVO的回调方法(系统提供的回调方法)
/**
* KVO的回调方法
@param keyPath 属性名称
@param object 被观察的对象
@param change 变化前后的值都存储在change字典中
@param context 注册观察者的时候,context传递过来的值
*/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
id oldW = [change objectForKey:NSKeyValueChangeOldKey];
NSLog(@"old--%@",oldW);
id newW = [change objectForKey:NSKeyValueChangeNewKey];
NSLog(@"new--%@",newW);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self.teacher setValue:@"75Kg" forKeyPath:@"student.weight"];
}
- (void)dealloc {
[self.teacher removeObserver:self forKeyPath:@"student.weight"];
self.teacher = nil;
}
从上面可以看到KVC赋值取值都有两种方式:
1、key的值必须存在且正确,如果拼写错误,会出现异常;
2、setValue: forKey:\ forKeyPath: 是根据key的值来写入对象的属性;valueForKey\ valueForKeyPath 方法根据key的值读取出对象的属性;
3、可以看出使用valueForKeyPath \ setValue:forKeyPth好一点,通过点的方式找属性符合开发习惯的。
4、在xib/Storyboard中,也可以使用KVC,下面是在xib中使用KVC把图片边框设置成圆角等:
关于系统中KVC应用例如:
// 1.最常见的修改textField里placehold的字体颜色和大小
[textField setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];
[textField setValue:[UIFont boldSystemFontOfSize:16] forKeyPath:@"_placeholderLabel.font"];
// 2.通过KVC设置UIPageControl自定义图片修改
UIPageControl *pageControl = [[UIPageControl alloc] init];
[pageControl setValue:[UIImage imageNamed:@"xx1 "] forKeyPath:@"_pageImage"];
[pageControl setValue:[UIImage imageNamed:@"xx2"] forKeyPath:@"_currentPageImage"];