iOS中的 观察者模式 之 KVO
Posted iOS软件开发之路
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS中的 观察者模式 之 KVO相关的知识,希望对你有一定的参考价值。
1、KVO的简介
KVO 全称 Key-Value Observing。中文叫键值观察。KVO其实是一种观察者模式,观察者在键值改变时会得到通知,利用它可以很容易实现视图组件和数据模型的分离,当数据模型的属性值改变之后作为监听器的视图组件就会被激发,激发时就会回调监听器自身。相比Notification,KVO更加的简单直接。
KVO的操作方法由NSKeyValueCoding提供,而他是NSObject的类别,也就是说ObjC中几乎所有的对象都支持KVO操作。
KVO 需要实现实例变量的 setter/getter方法
2、KVO的实现
0) KVO的使用也很简单,就是简单的3步。
(1)注册需要观察的对象的属性addObserver:forKeyPath:options:context:
(2)实现observeValueForKeyPath:ofObject:change:context:方法,这个方法当观察的属性变化时会自动调用.在这个方法中还通过 NSKeyValueObservingOptionNew这个参数要求把新值在dictionary中传递过来。
(3)取消注册观察removeObserver:forKeyPath:context:
1)注册
实现方法:
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
方法参数:
object : 被观察对象
observer: 观察对象
forKeyPath里面带上property的name,如UIView的frame、center等等
options: 有4个值,分别是:
NSKeyValueObservingOptionNew 把更改之前的值提供给处理方法
NSKeyValueObservingOptionOld 把更改之后的值提供给处理方法
NSKeyValueObservingOptionInitial 把初始化的值提供给处理方法,一旦注册,立马就会调用一次。通常它会带有新值,而不会带有旧值。
NSKeyValueObservingOptionPrior 分2次调用。在值改变之前和值改变之后。
注:例子里的0就代表不带任何参数进去
context: 可以带入一些参数,其实这个挺好用的,任何类型都可以,自己强转就好了。
2)监测
实现方法:
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSString*, id> *)change context:(nullable void *)context;
这个方法当观察的属性变化时会自动调用.在这个方法中还通过NSKeyValueObservingOptionNew这个参数要求把新值在dictionary中传递过来。
方法参数:
keyPath: 对应forKeyPath
object: 被观察的对象
change: 对应options里的NSKeyValueObservingOptionNew、NSKeyValueObservingOptionOld等
context: 对应context
3)取消注册观察
实现方法:
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(nullable void *)context NS_AVAILABLE(10_7, 5_0);
方法参数:
同上
3、具体实现代码
main.m
1 // 2 // main.m 3 // KVO具体实现 4 // 5 // Created by ma c on 16/5/18. 6 // Copyright © 2016年 彭盛凇. All rights reserved. 7 // 8 9 #import <Foundation/Foundation.h> 10 #import "Person.h" 11 #import "Watch.h" 12 13 int main(int argc, const char * argv[]) { 14 @autoreleasepool { 15 16 Person *person = [[Person alloc] init]; 17 18 Watch *watch = [[Watch alloc] init]; 19 20 person.watch = watch; 21 22 [person registerObserver]; 23 24 person.name = @"pss"; 25 26 person.name = @"pbb"; 27 28 } 29 return 0; 30 }
person.h
1 // 2 // Person.h 3 // KVO具体实现 4 // 5 // Created by ma c on 16/5/18. 6 // Copyright © 2016年 彭盛凇. All rights reserved. 7 // 8 9 #import <Foundation/Foundation.h> 10 #import "Watch.h" 11 12 @interface Person : NSObject 13 14 @property (nonatomic, copy) NSString *name; 15 16 @property (nonatomic, strong) Watch *watch; 17 18 - (void)registerObserver; 19 20 @end
Person.m
1 // 2 // Person.m 3 // KVO具体实现 4 // 5 // Created by ma c on 16/5/18. 6 // Copyright © 2016年 彭盛凇. All rights reserved. 7 // 8 9 #import "Person.h" 10 11 @implementation Person 12 13 - (void)dealloc { 14 15 [self removeObserver:self.watch forKeyPath:@"name"]; 16 } 17 18 - (void)registerObserver { 19 20 [self addObserver:self.watch forKeyPath:@"name" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil]; 21 } 22 @end
Watch.m
1 // 2 // Watch.m 3 // KVO具体实现 4 // 5 // Created by ma c on 16/5/18. 6 // Copyright © 2016年 彭盛凇. All rights reserved. 7 // 8 9 #import "Watch.h" 10 11 @implementation Watch 12 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context { 13 14 NSLog(@"new = %@",[change valueForKey:@"new"]); 15 16 NSLog(@"old = %@",[change valueForKey:@"old"]); 17 18 NSLog(@"---------------------------"); 19 } 20 @end
Log打印日志
4、KVO常见崩溃错误与解决方式
1)没有在准确位置实现dealloc , 注册,删除,监听 操作方法
2)没有实现dealloc中的remove操作
5、手动实现KVO
1 + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key { 2 3 BOOL automatic = YES; 4 if ([key isEqualToString:@"age"]) { 5 automatic = NO; 6 } else { 7 automatic = [super automaticallyNotifiesObserversForKey:key]; 8 } 9 10 return automatic; 11 } 12 13 - (void)setAge:(int)age { 14 15 //手动设置KVO 16 17 if (_age != age) { 18 19 [self willChangeValueForKey:@"age"]; 20 21 _age = age; 22 23 [self didChangeValueForKey:@"age"]; 24 25 } 26 }
6、KVO的实现原理
当某一个类的实例第一次使用KVO的时候,系统就会在运行期间动态的创建该类的一个派生类,该类的命名规则一般是以NSKVONotifying为前缀,以原本的类名为后缀。并且将原型的对象的isa指针指向该派生类。同时在派生类中重载了使用KVO的属性的setter方法,在重载的setter方法中实现真正的通知机制,正如前面我们手动实现KVO一样。这么做是基于设置属性会调用 setter 方法,而通过重写就获得了 KVO 需要的通知机制。当然前提是要通过遵循 KVO 的属性设置方式来变更属性值,如果仅是直接修改属性对应的成员变量,是无法实现 KVO 的。
以上是关于iOS中的 观察者模式 之 KVO的主要内容,如果未能解决你的问题,请参考以下文章