一、addObserver:forkeyPath:options:context:各个参数的作用分别是什么?observer中需要实现哪个方法才能获得KVO回调?
iOS中通知方面的内容:NSNotificationCenter通知中心、KVO(Key-Value Observing)键值观察 、Delegate代理、Callback回调。
KVO是Key-Value Observing的缩写,是对象间监控对方状态的改变,并做出反应的机制。对象可以为自己的属性注册观察者,当这个属性的值发生了改变,系统会对这些注册的观察者做出通知。
1、为对象的属性注册观察者。
- (void)addObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
context:(void *)context
observer:观察者对象。其必须实现方法observeValueForKeyPath:ofObject:change:context:。
keyPath:被观察的属性,其不能为nil。
options:设定通知观察者时传递的属性值,是传改变前的还是改变后的,通常设置为NSKeyValueObservingOptionNew。
context:一些其他的需要传递给观察者的上下文信息,通常设置为nil。
比如:
Person *myPerson = [[Person alloc] init];
[myPerson addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
2、观察者接收通知,观察者通过实现下面的方法,完成对属性改变的响应。
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
keyPath:被观察的属性,其不能为nil。
object:被观察者的对象。
change:属性值,根据上面提到的Options设置,给出对应的属性值。
context:注册观察者传递的context对象。
比如:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:@"age"]) {
NSLog(@"age is changed age = %@", [change valueForKey:NSKeyValueChangeNewKey]);
}
else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
KVO的回调要被调用,属性必须是通过KVC的方法来修改的,如果是调用类的其他方法来修改属性,这个观察者是不会得到通知的。
3、清除观察者。
- (void)removeObserver:(NSObject *)anObserver forKeyPath:(NSString *)keyPath
摘抄(http://ningandjiao.iteye.com/blog/2009729):
当一个观察者观察多个对象的相同属性(即不同Object,但是KeyPath相同),可通过设定静态的Context变量来区分不同的通知。
使用NSStringFromSelector(@selector(method))来获取KeyPath,而不是直接通过NSString写属性名,这样编译器可以帮助发现属性名中的Typo。
通过方法:+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key,通过一个Key观察多个属性值的改变。
二、如何手动触发一个value的KVO?
自动触发是指类似这种场景:在注册 KVO 之前设置一个初始值,注册之后,设置一个不一样的值,就可以触发了。
想知道如何手动触发,必须知道自动触发 KVO 的原理:
键值观察通知依赖于 NSObject 的两个方法: willChangeValueForKey: 和 didChangevlueForKey: 。在一个被观察属性发生改变之前, willChangeValueForKey: 一定会被调用,这就
会记录旧的值。而当改变发生后, didChangeValueForKey: 会被调用,继而 observeValueForKey:ofObject:change:context: 也会被调用。如果可以手动实现这些调用,就可以实现“手动触发”了。
转自<<猿圈>>
三、若一个类有实例变量NSString *_foo,调用setValue:forKey:时,可以以foo还是_foo作为key?
都可以。
四、KVC的keyPath中的集合运算符如何使用?
KVC中的集合运算符有以下三类:
1、简单集合运算符,只能用在集合对象中,对象属性必须为数字类型。
@avg、@sum、@max、@min、@count
2、对象操作符。
@unionOfObjects:返回指定属性的值的数组,不去重。
@distinctUnionOfObjects:返回指定属性去重后的值的数组。
3、数组/集体操作符。
@unionOfArrays:返回一个数组,值由各个子数组的元素组成,不去重。
@distinctUnionOfArrays:返回一个数组,值由各个子数组的元素组成,去重。
@distinctUnionOfSets:和distinctUnionOfArrays相似,NSSet不能有重复的值。
//
// Person.h
// KVC集合运算符
//
// Created by hkshen on 2017/9/8.
// Copyright © 2017年 hkshen. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface Person : NSObject
// 姓名
@property (nonatomic, copy) NSString *name;
// 年龄
@property (nonatomic, assign) NSInteger age;
// 设置属性变量方法
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age;
@end
//
// Person.m
// KVC集合运算符
//
// Created by hkshen on 2017/9/8.
// Copyright © 2017年 hkshen. All rights reserved.
//
#import "Person.h"
@implementation Person
//不写@synthesize name = _name的话,默认实例变量为_name
@synthesize name = _name;
@synthesize age = _age;
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age {
if (self = [super init]) {
_name = name;
_age = age;
}
return self;
}
@end
//
// ViewController.m
// KVC集合运算符
//
// Created by hkshen on 2017/9/8.
// Copyright © 2017年 hkshen. All rights reserved.
//
#import "ViewController.h"
#import "Person.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
Person *person1 = [[Person alloc] initWithName:@"maozedong" age:99];
Person *person2 = [[Person alloc] initWithName:@"maozedong" age:98];
Person *person3 = [[Person alloc] initWithName:@"baoqingtian" age:97];
Person *person4 = [[Person alloc] initWithName:@"qinshihuang" age:96];
Person *person5 = [[Person alloc] initWithName:@"lishiming" age:95];
Person *person6 = [[Person alloc] initWithName:@"kangxi" age:94];
// 简单集合运算符不能用于单个对象中,一下写法crash
//int oneAgeSum = [[person1 valueForKeyPath:@"@sum.age"] intValue];
//NSLog(@"%d", oneAgeSum);
NSArray *personArrayHK = @[person1, person2, person3, person4, person5, person6];
// 简单集合运算符
int personAgeAvg = [[personArrayHK valueForKeyPath:@"@avg.age"] intValue];
int personAgeSum = [[personArrayHK valueForKeyPath:@"@sum.age"] intValue];
int personAgeMax = [[personArrayHK valueForKeyPath:@"@max.age"] intValue];
int personAgeMin = [[personArrayHK valueForKeyPath:@"@min.age"] intValue];
int personAgeCount = [[personArrayHK valueForKeyPath:@"@count.age"] intValue];
// 对象操作符:对数组对象进行操作
NSArray<NSString *> *personNameArray1 = [personArrayHK valueForKeyPath:@"@unionOfObjects.name"]; // 不去重
NSArray<NSString *> *personNameArray2 = [personArrayHK valueForKeyPath:@"@distinctUnionOfObjects.name"]; // 去重
Person *person7 = [[Person alloc] initWithName:@"jiangzeming" age:96];
Person *person8 = [[Person alloc] initWithName:@"jiangzeming" age:95];
Person *person9 = [[Person alloc] initWithName:@"zhurongji" age:94];
NSArray *personArrayWL = @[person7, person8, person9];
// 数组/集体操作符:对由NSArray和NSSet所组成的集合操作
// 需要两个数组组成一个数组
NSArray *personNameArray3 = [@[personArrayHK, personArrayWL] valueForKeyPath:@"@unionOfArrays.name"]; // 不去重
NSArray *personNameArray4 = [@[personArrayHK, personArrayWL] valueForKeyPath:@"@distinctUnionOfArrays.name"]; // 去重
NSLog(@"personAgeAvg:%d", personAgeAvg);
NSLog(@"personAgeSum:%d", personAgeSum);
NSLog(@"personAgeMax:%d", personAgeMax);
NSLog(@"personAgeMin:%d", personAgeMin);
NSLog(@"personAgeCount:%d", personAgeCount);
NSLog(@"personNameArray1:%@", personNameArray1);
NSLog(@"personNameArray2:%@", personNameArray2);
NSLog(@"personNameArray3:%@", personNameArray3);
NSLog(@"personNameArray4:%@", personNameArray4);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
打印:
2017-09-08 18:12:34.384 KVC集合运算符[36056:3211801] personAgeAvg:96
2017-09-08 18:12:34.384 KVC集合运算符[36056:3211801] personAgeSum:579
2017-09-08 18:12:34.385 KVC集合运算符[36056:3211801] personAgeMax:99
2017-09-08 18:12:34.385 KVC集合运算符[36056:3211801] personAgeMin:94
2017-09-08 18:12:34.385 KVC集合运算符[36056:3211801] personAgeCount:6
2017-09-08 18:12:34.385 KVC集合运算符[36056:3211801] personNameArray1:(
maozedong,
maozedong,
baoqingtian,
qinshihuang,
lishiming,
kangxi
)
2017-09-08 18:12:34.385 KVC集合运算符[36056:3211801] personNameArray2:(
baoqingtian,
maozedong,
lishiming,
qinshihuang,
kangxi
)
2017-09-08 18:12:34.385 KVC集合运算符[36056:3211801] personNameArray3:(
maozedong,
maozedong,
baoqingtian,
qinshihuang,
lishiming,
kangxi,
jiangzeming,
jiangzeming,
zhurongji
)
2017-09-08 18:12:34.386 KVC集合运算符[36056:3211801] personNameArray4:(
lishiming,
kangxi,
jiangzeming,
maozedong,
zhurongji,
baoqingtian,
qinshihuang
)
github:点击打开链接
五、如何关闭默认的KVO的默认实现,并进入自定义的KVO实现?
http://tech.glowing.com/cn/implement-kvo/
http://blog.youkuaiyun.com/bravegogo/article/details/50699594