IOS SDK详解之KVO

来源:互联网 发布:淘宝卖人参 编辑:程序博客网 时间:2024/05/21 22:06

原创Blog,转载请注明出处
blog.csdn.net/hello_hwc


前言:KVC和KVO是帮助我们驾驭objective C动态特性工具。KVO是建立在KVC基础上的,所以不了解KVC的同学可以参见我的这篇博客。这里我不会再重复讲解KVC。
http://blog.csdn.net/hello_hwc/article/details/43769765
本文的内容

KVO的定义
KVO的典型使用场景。
手动KVO
几点KVO要说的地方


一 KVO的定义

KVO提供了一种key-value-observing的机制,也就是说可以通过监听key,来获得value的变化。用来在对象之间监听状态变化。使用KVO的类要遵循 协议,事实上,任何继承自NSObject的类,都遵循了这个协议。而Object C中,几乎所有的类都源自NSObject

使用KVO通常分为三步

1.1 订阅想要监听的keypath

用函数

- (void)addObserver:(NSObject *)anObserver         forKeyPath:(NSString *)keyPath            options:(NSKeyValueObservingOptions)options            context:(void *)context

注册通知

  • observer:观察者,也就是KVO通知的订阅者。订阅着必须实现
    observeValueForKeyPath:ofObject:change:context:方法
  • keyPath:描述将要观察的属性,相对于被观察者。
  • options:KVO的一些属性配置;有四个选项。
  • context: 上下文,这个会传递到订阅着的函数中,用来区分消息,所以应当是不同的。

options所包括的内容

  • NSKeyValueObservingOptionNew:change字典包括改变后的值
  • NSKeyValueObservingOptionOld:change字典包括改变前的值
  • NSKeyValueObservingOptionInitial:注册后立刻触发KVO通知
  • NSKeyValueObservingOptionPrior:值改变前是否也要通知(这个key决定了是否在改变前改变后通知两次)

1.2 响应状态变化

每当监听的keyPath发生变化了,就会在这个函数中回调。

- (void)observeValueForKeyPath:(NSString *)keyPath                      ofObject:(id)object                        change:(NSDictionary *)change                       context:(void *)context
  • keyPath:被监听的keyPath , 用来区分不同的KVO监听。
  • object: 被观察修改后的对象(可以通过object获得修改后的值)
  • change:保存信息改变的字典(可能有旧的值,新的值等)
  • context:上下文,用来区分不同的KVO监听。

1.3 在适当的时候,取消订阅

通常使用两个函数

- (void)removeObserver:(NSObject *)anObserver            forKeyPath:(NSString *)keyPath- (void)removeObserver:(NSObject *)observer            forKeyPath:(NSString *)keyPath               context:(void *)context

一般在remove的时候会这么写,因为remove的时候,无法判读啊remove是否成功了

    @try {        [object removeObserver:self forKeyPath:keyPath];    }    @catch (NSException *exception) {        NSLog(@"%@",exception);    }

二 KVO的典型使用场景 - model 与 view的同步

这里,你将看到一个完整的KVO的例子。和上篇KVC一样,我写了个类似的demo。点击random会随机改变User的age,然后UI上要进行同步显示出新的和旧的age。
这里写图片描述

实现过程如下:
定义了一个User类来作为Model

@interface User : NSObject@property (strong,nonatomic) NSString * name;@property (nonatomic) NSUInteger age;@end

定义两个静态的变量,一个作为keyPath,一个作为context

static  NSString * observename = @"age";static void * privateContext = 0;

然后在viewWillAppear中注册(订阅)KVO,在viewWillDisappear中删除KVO

-(void)viewWillAppear:(BOOL)animated{    [self.user addObserver:self                forKeyPath:observename                   options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:privateContext];}-(void)viewWillDisappear:(BOOL)animated{    @try {        [self.user removeObserver:self forKeyPath:observename];    }    @catch (NSException *exception) {        NSLog(@"%@",exception);    }}

当点击random的时候,age会改变

- (IBAction)random:(id)sender {    self.user.age = arc4random()%100 +1;}

然后,在上面提到的函数中进行model和view的同步

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{    if (context == privateContext) {        if ([keyPath isEqualToString:observename]) {            NSNumber * old = [change objectForKey:NSKeyValueChangeOldKey];            NSNumber * new = [change objectForKey:NSKeyValueChangeNewKey];            self.lastvalue.text = [NSString stringWithFormat:@"%@",old];            self.newvalue.text = [NSString stringWithFormat:@"%@",new];        }    }}

三 手动KVO

KVO的实现,是对注册的keyPath中自动实现了两个函数,在Setter中,自动调用。

- (void)willChangeValueForKey:(NSString *)key- (void)didChangeValueForKey:(NSString *)key

可能有时候,我们要实现手动的KVO
这时候需要关闭自动生成KVO通知,然后手动的调用,手动通知的好处就是,可以灵活加上自己想要的判断条件。例如

+(BOOL)automaticallyNotifiesObserversOfAge{    return NO;}-(void)setAge:(NSUInteger)age{    if (age < 22) {        return;    }    [self willChangeValueForKey:@"age"];    _age = age;    [self didChangeValueForKey:@"age"];}

四 KVO要提到的几点

KVO和Context

由于Context通常用来区分不同的KVO,所以context的唯一性很重要。通常,我的使用方式是通过在当前.m文件里用静态变量定义。

static void * privateContext = 0;

KVO与线程

KVO的响应和KVO观察的值变化是在一个线程上的,所以,大多数时候,不要把KVO与多线程混合起来。除非能够保证所有的观察者都能线程安全的处理KVO

监听变化的值

改变前和改变后分别为

id oldValue = change[NSKeyValueChangeOldKey];id newValue = change[NSKeyValueChangeNewKey];

3 0
原创粉丝点击