1.链接:https://www.jianshu.com/p/badf5cac0130
2.链接地址:.https://tech.glowing.com/cn/implement-kvo/
3.我的链接地址 https://github.com/aofeilin/kvoDemo
1.KVO的实现原理:
NSObject()提供了一个协议,的默认实现,它为所有对象提供了一种自动发送修改通知的能力,我们可以通过禁用,自己手动去观察值的变化。
观察者设计模式,
1.指定一个被观察对象,A类,当对象某个属性,发生改变时,对戏那个会获得通知,并作出相应处理,
且不需要给被观察的对象添加任何额外代码,就能使用KVO机制。
2.在 MVC 设计架构下的项目,KVO 机制很适合实现 mode 模型和 view 视图之间的通讯。
例如:代码中,在模型类A创建属性数据,在控制器中创建观察者,一旦属性数据发生改变就收到观察者收到通知,通过 KVO 再在控制器使用回调方法处理实现视图 B 的更新;(本文中的应用就是这样的例子.)
2.手动控制自动监听。
[self.person willChangeValueForKey:@"name"];
self.person.name = @"我的new 名字";
[self.person didChangeValueForKey:@"name"];
3.实现原理。
KVO会重写keyPath对应属性的setter方法,没有被KVO的属性则不会重写其setter方法。在重写的setter方法中,修改值之前会调用willChangeValueForKey:方法,修改值之后会调用didChangeValueForKey:方法,这两个方法最终都会被 调用到observeValueForKeyPath:ofObject:change:context:方法中
4.依赖属性。
//4.那么当firstName和lastName改动的时候,该值必须被通知。这是一种依赖方法。==
//+ (NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key{
// NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
// if ([key isEqualToString:@"fullName"]) {
// NSSet *effectiingKeys = [NSSet setWithObjects:@"lastName",@"firstName", nil];
// keyPaths = [keyPaths setByAddingObjectsFromSet:effectiingKeys];
// }
// return keyPaths;
//}
//4.那么当firstName和lastName改动的时候,该值必须被通知。这是一种依赖方法。==
- (NSSet<NSString *> *)keyPathsForValuesAffectingFullName
{
return [NSSet setWithObjects:@"lastName",@"firstName", nil];
}
5.缺点,自己实现。
//5.1.缺点:1.重复add remove 导致crash,observer释放导致的崩溃,keyPath 传错导致的崩溃,在调用KVO时需要传入一个keyPath
//5.2.NSStringFromSelector(@selector(isFinished))
//5.3.不支持block语法,
//5.4.自己实现。
1.简单的使用?
2.验证它的原理。查看他的原理。
3.缺点。
4.自己优化。
//
// ViewController.m
// KVCDemoProject
//
// Created by aofeilin on 2018/11/20.
// Copyright © 2018年 com.aofeilin.com. All rights reserved.
//
#import "ViewController.h"
#import "Person.h"
#import "NSObject+KVO.h"
@interface ViewController ()
@property (nonatomic,strong)Person *person;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//1.注册观察者
self.person = [[Person alloc]init];
[self.person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
//1.1 NSKeyValueObservingOptionNew
/*
2018-11-20 16:21:40.847239+0800 KVCDemoProject[2899:497961] change:{
kind = 1;
new = 18;
}
*/
//1.2 NSKeyValueObservingOptionInitial
//1.3 NSKeyValueObservingOptionOld
/*
2018-11-20 16:27:09.705258+0800 KVCDemoProject[3011:509590] change:{
kind = 1;
old = "<null>";
}
*/
//1.4 NSKeyValueObservingOptionPrior
/*
2018-11-20 16:28:04.816669+0800 KVCDemoProject[3057:512098] change:{
kind = 1;
notificationIsPrior = 1;
}
2018-11-20 16:28:04.816781+0800 KVCDemoProject[3057:512098] context:(null)
2018-11-20 16:28:04.816919+0800 KVCDemoProject[3057:512098] keyPath:age
2018-11-20 16:28:04.817045+0800 KVCDemoProject[3057:512098] object:<Person: 0x600001bc06a0>
2018-11-20 16:28:04.817240+0800 KVCDemoProject[3057:512098] change:{
kind = 1;
}
*/
self.person.age = @"18";
//2.手动控制自动通知
self.person.name = @"aofeilin";
//4.依赖属性
[self.person addObserver:self forKeyPath:@"fullName" options:NSKeyValueObservingOptionNew context:nil];
self.person.firstName = @"WANG";
self.person.lastName = @"Gang";
self.person.firstName = @"LI";
self.person.lastName = @"QIANG";
//5.实现原理----
//5.1.缺点:1.重复add remove 导致crash,observer释放导致的崩溃,keyPath 传错导致的崩溃,在调用KVO时需要传入一个keyPath
//5.2.NSStringFromSelector(@selector(isFinished))
//5.3.不支持block语法,
//5.4.自己实现。
[self.person PG_addObserver:self forKey:NSStringFromSelector(@selector(content))
withBlock:^(id observedObject, NSString *observedKey, id oldValue, id newValue) {
NSLog(@"%@.%@ is now: %@", observedObject, observedKey, newValue);
dispatch_async(dispatch_get_main_queue(), ^{
self.textField.text = newValue;
});
}];
[self buttonAction:nil];
}
//3.手动通知的触发 如果 automaticallyNotifiesObserversForKey name属性 return NO.
- (IBAction)buttonAction:(id)sender {
NSArray *msgs = @[@"Hello World!", @"Objective C", @"Swift", @"Peng Gu", @"peng.gu@me.com", @"www.gupeng.me", @"glowing.com"];
NSUInteger index = arc4random_uniform((u_int32_t)msgs.count);
self.person.content = msgs[index];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
NSLog(@"keyPath:%@",keyPath);
NSLog(@"object:%@",object);
NSLog(@"change:%@",change);
NSLog(@"context:%@",context);
}
-(void)dealloc{
//remove 注册
}
@end