博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
KVO知识点
阅读量:6092 次
发布时间:2019-06-20

本文共 4103 字,大约阅读时间需要 13 分钟。

  1. 被观察的对象必须兼容KVO。一般情况下,如果对象是继承自NSObject的话,那么该对象及其属性都会自动兼容KVO。
  2. 观察者调用addObserver:forKeyPath:options:context:方法来将自身添加为观察者,观察对象的key path
  3. 为了获取被观察对象的key path的变化,观察者还需要实现observeValueForKeyPath:ofObject:change:context:
  4. 在结束观察(比方说对象被释放等情况)的话,观察者需要调用removeObserver:forKeyPath:来移除观察。
  5. KVO跟NSNotificationCenter不同的一点是,KVO没有一个集中的对象来提供通知给所有观察者。在被观察的对象发生变化时,KVO会直接给观察者发送消息,没有其他多余的操作。

addObserver:forKeyPath:options:context:

  • options:

    • NSKeyValueObservingOptionOld: 获取改变之前的值
    • NSKeyValueObservingOptionNew: 获取改变之后的值
    • NSKeyValueObservingOptionInitial: 当某个属性进行了初始化时发送通知。只会接收到一次
    • NSKeyValueObservingOptionPrior: 在发生变回之前发送通知
  • context:

    可以赋值为NULL,但是如果观察者的父类也同样观察者同一个key path时,会出现问题。

    为了避免问题的发生,最好的方式是在类中创建一个静态变量来作为context.

    static void *PersonAccountBalanceContext = &PersonAccountBalanceContext;static void *PersonAccountInterestRateContext = &PersonAccountInterestRateContext;复制代码

observeValueForKeyPath:ofObject:change:context:

所有观察者都必须实现此方法来接收通知!


removeObserver:forKeyPath:context:

  1. 如果你的对象没有被注册为观察者,而你又调用了移除观察者的方法的话,会出现NSRangeException错误。避免出现此错误的话,最好是注册和移除的方法配套使用,或者是将移除的方法放在try/catch中执行。
  2. 观察者对象在被摧毁时并不会自动移除自身的观察者身份。
  3. 通常在init或者viewDidLoad中注册观察者身份,在dealloc中移除。

自动发送消息

e.g. 能够引起KVO消息发送的例子

// 调用访问器方法[account setName:@"Savings"];// 使用 setValue:forKey:[account setValue:@"Savings" forKey:@"name"];// 使用 setValue:forKeyPath:[document setValue:@"Savings" forKeyPath:@"account.name"];复制代码

手动发送消息

某些情况下,你可能会想自己来管理消息的进程。比方说因为某些原因减少触发的消息数,又或者将几个通知合并为一个。要完成上面的需求的话,你就需要手动触发KVO的消息发送。

手动和自动发送消息并不会冲突。一般情况下,我们只会对某一个特殊的对象进行手动的消息处理。这样的话,我们在继承NSObject的时候,需要复写automaticallyNotifiesObserversForKey:方法,并且返回NO.

e.g.

+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {BOOL automatic = NO;if ([theKey isEqualToString:@"balance"]) {automatic = NO;}else {automatic = [super automaticallyNotifiesObserversForKey:theKey];}return automatic;}复制代码

然后,在该属性的访问器方法中,调用willChangeValueForKeydidChangeValueForKey

- (void)setBalance:(double)theBalance {  [self willChangeValueForKey:@"balance"];  _balance = theBalance;  [self didChangeValueForKey:@"balance"];}// or- (void)setBalance:(double)theBalance {  if (theBalance != _balance) {    [self willChangeValueForKey:@"balance"];    _balance = theBalance;    [self didChangeValueForKey:@"balance"];  }}// 如果是一对多的关系的话,还需要修改对象改变的类型(NSKeyValueChangeInsertion, NSKeyValueChangeRemoval, 或者 NSKeyValueChangeReplacement)以及所涉及到index- (void)removeTransactionsAtIndexes:(NSIndexSet *)indexes {  [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:@"transactions"];// Remove the transaction objects at the specified indexes.  [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:@"transactions"];}复制代码

  • To-One RelationShips

要自动触发一对一关系的通知的话,需要复写keyPathsForValuesAffectingValueForKey:

比方说有一个fullName的属性

- (NSString *)fullName {  return [NSString stringWithFormat:@"%@ %@", firstName, lastName];}复制代码

fullName的属性由firstName和lastName决定。在复写的时候,需要指定这两个相关的属性

+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {  NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];    if ([key isEqualToString:@"fullName"]) {    NSArray *affectingKeys = @[@"lastName", @"firstName"];    keyPaths = [keyPaths setByAppendingObjectsFromArray:affectingKeys];  }  return keyPaths;}// 可以更改为+ (NSSet *)keyPathsForValuesAffectingFullName {  return [NSSet setWithObjects:@"lastName", @"firstName", nil];}复制代码
  • To-Many RelationShips
  1. 在被观察者的相关属性中注册观察者。
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {  if (context == totalSalaryContext) {    [self updateTotalSalary];  } else    // deal with other observations and/or invoke super...}- (void)updateTotalSalary {  [self setTotalSalary:[self valueForKeyPath:@"employees.@sum.salary"]];}- (void)setTotalSalary:(NSNumber *)newTotalSalary {  if (totalSalary != newTotalSalary) {    [self willChangeValueForKey:@"totalSalary"];    _totalSalary = newTotalSalary;    [self didChangeValueForKey:@"totalSalary"];  }}- (NSNumber *)totalSalary {  return _totalSalary;}复制代码
  1. 如果使用Core Data的话,可以将managed context object注册为观察者。

转载于:https://juejin.im/post/5a30e0896fb9a0451543df6a

你可能感兴趣的文章
E媒体|APP必死?!——阿里百川项目总监承渊有话说
查看>>
Android 游戏之三人对战源码
查看>>
Lua与C++交互机制
查看>>
MDT2012制作模板机
查看>>
我的友情链接
查看>>
关于海量数据的数据模型
查看>>
缓存重要
查看>>
我的友情链接
查看>>
MYSQL驱动包升级到5.1.17版本之后会出现的问题
查看>>
我的友情链接
查看>>
我的友情链接
查看>>
特殊权限-SUID,SGID,Sticky 学习笔记
查看>>
手工添加一个linux用户并能登陆
查看>>
影响网络营销成功的3大因素
查看>>
Linux负载均衡软件LVS之一(概念篇)
查看>>
微信app支付php服务端轮子
查看>>
自适应网页
查看>>
python winrm 连接windows
查看>>
十年等待,幸福人生
查看>>
思科路由器由于IP INPUT进程导致cpu负荷高的判断方法
查看>>