zoukankan      html  css  js  c++  java
  • 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 }
    main.m

      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.h

      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
    Person.m

      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
    Watch.h

      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 的。

  • 相关阅读:
    P4165 [SCOI2007]组队
    CF575B
    SG函数胡乱一气
    CF280C Game on Tree
    AGC010F
    CF140E
    多功能的低功耗蓝牙可穿戴设备用于监测血液酒精浓度
    智能手环幕后的英雄
    图像处理实用资源
    15.12DataGridView分页显示
  • 原文地址:https://www.cnblogs.com/PSSSCode/p/5506577.html
Copyright © 2011-2022 走看看