iOS KVO学习笔记

KVO是Key-Value Observing的简称,翻译过来就是键值对观察。项目中主要用于观察某个变量是否发生变化并通知主线程及时根据变化来更新UI。这里存在观察者和被观察者两个概念,会在下面结合代码来进行说明。

KVO分为自动通知和手动通知,首先来说一下自动通知:

例如我们有两个类对象A和B,A的代码如下

@interface A:NSObject

@property (strong, nonatomic) NSString *paramA;
@property (strong, nonatomic) B *b;
B的代码如下:

@interface B:NSObject

@property (strong, nonatomic) NSString *paramB;
A中有一个B的对象作为A的一个成员变量,如果我们想让A成为b的观察者,即让A对b的变化做出反应,那么在声明观察者时应该这样:

[b addObserver:self forKeyPath:@"paramB" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];


因为声明是在A中,所以self即指A。这句话的意思是当b的paramB发生变化时A会调用方法:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context

例如[b setValue:@"2" forKey:@"paramB"],keyPath为paramB,object为b,change中会有三个键值对,分别为kind,new,old;old和new分别对应变化前和变化后的paramB的值,这里的keyPath不能随便写,必须是被观察者的一个成员变量。

反过来,如果我们想让b成为A的观察者,例如我们想在b中观察A中paramA的变化,那么在A中声明观察者时应该这样:

[self addObserver:b forKeyPath:@"paramA" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil
]
然后
[self setValue:@"2" forKey:@"paramA"]
这次我们需要在B中实现方法

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
因为b是观察者,A是被观察者,所以信息会发送到观察者中。这时keyPath为paramA,object为A的对象,change中依然会有三个键值对:kind,new,old; old和new分别对应变化前和变化后的paramA的值。

但是自动通知只要调用setValue方法就会通知,如果变化前和变化后值没有变化它也会去通知,这样就会浪费资源了,这时就可以使用手动通知了。手动通知需要在观察者的类中实现方法:

+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key
如果我们想把上文中keyPath为paramA的通知改为手动通知,我们需要在B中添加代码:
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key{
    if([key isequaltostring:@"paramA"]){
        return NO;
}
   return YES;
}
这里就是告诉B,如果有keyPath叫paramA的通知,那么你不用管了,我们自己会调用通知方法。这里如果我们再在A中通过setValue改变paramA的值的话,

那么B中的方法

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
不会被调用,因为需要我们手动通知了,在A中正确的写法应该是:

[self willChange<span style="font-family: Arial, Helvetica, sans-serif;">Value</span>ForKey:@"paramA"];
[self setValue:@"2" forKey:@"paramA"];
/*
这里还可以添加一些自己的逻辑代码
*/
[self didChange<span style="font-family: Arial, Helvetica, sans-serif;">Value</span><span style="font-family: Arial, Helvetica, sans-serif;">ForKey:@"paramA"];</span>
手动通知发送通知遵循谁被观察谁发送的原则。其次不管是自动通知还是手动通知都需要注册通知,即都需要调用方法:

- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context;
使用完之后还需要移除这些观察者,通过方法:
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(void *)context ;
或者
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;
唯一的区别在于context,这个是注册观察者时也有的一个参数,同样的观察者和keyPath,如果context不一样,生成的对象也是不一样,但是context的用法目前还没弄清楚;所以为了防止因为删除了不存在的观察者造成crash,最好在调用上述方法时使用try catch。

以上所有改变值,特别是自动通知都是通过setValue方法,其实这个方法是KVC的,KVC全称是key-value coding ,所以KVO还是基于KVC。KVC的原理还没有看懂。






猜你喜欢

转载自blog.csdn.net/u010843426/article/details/43484877