KVO

2019-03-17  本文已影响0人  iOS_愛OS

KVO 简介

KVO 键值观察机制,就是观察指定对象的指定属性变化情况。

KVO 键值观察 依赖于 KVC 健值编码

Key-value observing 通常用于 MVC 中,model 与 controller直接的通讯。

继承于 NSObject 才可以拥有 KVO 机制

两种 KVO 应用方式

举个例子,一个 Person 类, 一个 Account

Account 的属性 余额 balance 改变时通知 Person
Account 添加观察者 Person
[account addObserver:person forKeyPath:@"balance" options:options context:context];

Person 观察 Account 的属性变化
Person 中实现观察者方法 observeValueForKeyPath:ofObject:change:context:

Account 移除观察者 Person
[account removeObserver:person forKeyPath:@"balance" context:context]


这篇文章的重要内容

  1. Registering for Key-Value Observing 注册键值观察的过程

  2. Registering Dependent Keys 对于存在 key 依赖的键值观察

  3. Key-Value Observing Implementation Details 键值观察的实现细节


1. Registering for Key-Value Observing 注册键值观察的过程

KVO 生命周期的过程,必须完成下面这三个方法

  1. 给被观察者注册观察者 addObserver:forKeyPath:options:context:.
  2. 在观察者里实现方法 接受通知 observeValueForKeyPath:ofObject:change:context:
  3. 移除观察者 removeObserver:forKeyPath: 要在观察者的内存销毁之前 移除观察者机制

- 注册观察者 addObserver:forKeyPath:options:context:.

- Receiving Notification of a Change 接收通知响应

        所有的观察者必须实现方法 `observeValueForKeyPath:ofObject:change:context: message.`

keyPath是个标量或者 c 语言结构体,会把这些包装成 NSNumberNSValue,

keyPath 是个容器, change中可能会包含这个容器 inserted,removed,replaced 的情况 对应的键有

- `NSKeyValueChangeInsertion`
- `NSKeyValueChangeRemoval` 
- `NSKeyValueChangeReplacement`

keypath 是个 NSIndexSet : change 中可能会包含数组

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context {
 
     if (context == PersonAccountBalanceContext) {
     // Do something with the balance…
      } else if (context == PersonAccountInterestRateContext) {
    // Do something with the interest rate…
      } else {
                    // Any unrecognized context must belong to super
      [super observeValueForKeyPath:keyPath
                                         ofObject:object
                                           change:change
                                           context:context];
       }
}

- Removing an Object as an Observer 移除观察者

观察者被移除之后就不会再接受到通知。

        - (void)unregisterAsObserverForAccount:(Account*)account {
            [account removeObserver:self
                         forKeyPath:@"balance"
                            context:PersonAccountBalanceContext];
         
            [account removeObserver:self
                         forKeyPath:@"interestRate"
                            context:PersonAccountInterestRateContext];
        }

需要注意的地方

Automatic Change Notification 自动 KVO 通知

NSObject 提供了自动健值更改通知的实现,自动 KVO 通知依赖于 KVC 编码机制获取, KVC method,和 集合代理(collection proxy)mutableArrayValueForKey:

手动 KVO

当你想要控制整个 KVO 的进程可以采用手动 KVO, 比如减少不必要的通知,比如将大量的通知控制到一个通知中。
手动 KVO 跟 自动 KVO 可以共存,比如同一个对象的同一个属性,可以在父类里自动 KVO, 在子类里手动 KVO, 重写方法 automaticallyNotifiesObserversForKey即可


2.KVO - 注册依赖 key

单个关系

重写下面两个方法中的一个即可

多个关系

比如,有个部门类 Department,有个员工类 Employees, 每个员工的薪水都会影响这个部门的总薪水。
可以在 Department 添加、删除 Employees 的时候,给 Employees 注册、移除观察者 Employees

用例如下

```
- (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;
}
```

3. KVO 内部实现细节

使用 isa-swizzling 原理
当 对象的属性注册到观察者中时,会创建一个中间类,重写了被观察属性的 setter 方法。

上一篇 下一篇

猜你喜欢

热点阅读