• KVO 开发详情


    目录

    • 概念

    • 应用KVO的3个步骤

    • 关联属性的KVO

    • 手动管理KVO通知

    一、概念

      KVO全称是 Key-Value Observing ,是OC的一种消息发送机制。这个机制是指:假设将B对象注册为A对象的观察者,当A对象的属性发生改变时就会通知它自己的所有观察者包括对象B在内。

      KVO一般用于Controller与Model之间的通信。常见的做法是把Controller注册为Model的观察者,当model改变时Controller就会收到通知并根据新的model来更新View。

      KVO是基于KVC技术的,所以为了理解KVO你必须先了解KVC(还不了解的请移步)。

      需要注意的是,只有两个方式改变对象的属性才会触发KVO通知

        1.通过KVC方法改变(setValue:forKey,setValue:forKeyPath)

        2.通过setter函数(.操作符会调用setter)

      当然你也可以手动管理KVO通知,我们在下面将会讲到

    二、应用KVO的3个步骤

    1. 为被观察者注册一个观察者
    2. 观察者接收属性改变的通知
    3. 移除观察者

      1.注册观察者  

    //这句代码的意思是 self.observing 观察 self.observed的age属性
    [self.observed addObserver:self.observing forKeyPath:@"age" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];

      2.接收通知

      在观察者类实现下面方法,当被观察的对象的属性改变时KVO就会调用这个方法。

    -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{
        
        NSLog(@"%@ : [%@] => [%@]",keyPath,[change valueForKey:NSKeyValueChangeOldKey],[change valueForKey:NSKeyValueChangeNewKey]);
        
    }

      3.移除观察者

      因为注册观察者时,被观察者就持有观察者的强引用,这有这样才能在属性改变时通知观察者。

    [self.observed removeObserver:self.observing forKeyPath:@"age"];

       我可以这样有下面代码改变属性,触发KVO通知

    // setter
    [self.observed setAge:@18];
    // kvc
    [self.observed setValue:@20 forKey:@"age"];

      如果不出意外应该会输出如果log

    age : [0] => [18]
    age : [18] => [20]

    三、关联属性的KVO  

      关联的属性分为两种情况,一对多,多对多

      多对多情况,我们只能通过手动触发KVO等方式来处理

      下面我们来介绍一对多的情况。

      假设Person类有一个readonly的属性 fullName,这个fullName有firstName和lastName组成,下面是fullName的getter函数

    -(NSString *)fullName{
        return [NSString stringWithFormat:@"%@ %@",self.firstName,_lastName];
    }

      我们希望当改变firstName或者lastName时可以触发fullName的kvo通知以便告诉其他人,他人只要观察fullName属性就可以了而不需要分别观察firstName跟lastName。

      我们可以有如下函数之一来告诉fullName与fristName、lastName的关联关系

    // 单个对象的属性 1
    +(NSSet *)keyPathsForValuesAffectingFullName{
        return [NSSet setWithArray:@[@"firstName",@"lastName"]];
    }
    // 单个对象的属性 2
    +(NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key{
        NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
        if ([key isEqualToString:@"fullName"]) {
            NSArray *affectingKeys = @[@"lastName", @"firstName"];
            keyPaths = [keyPaths setByAddingObjectsFromArray:affectingKeys];
        }
        return keyPaths;
    }

    四、手动管理KVO通知

      普通的对象之所以拥有KVO的功能是因为我们的对象都是继承自NSObject类的。

      NSObject帮我实现了一些函数, KVO在适当的时候通过调用这些函数来完成KVO功能。

      其中包过下面两个函数

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

      我们正式通过自己调用这两个函数来控制KVO的通知的发送,当我们想手动触发age属性的KVO通知时可以这样写:

    [self willChangeValueForKey:@"age"];
     _age = 18;
    [self didChangeValueForKey:@"age"];

      当然,最后你得告诉oc说你自己来管理KVO通知

    + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {
        if ([theKey isEqualToString:@"age"]) {
            return NO;
        }
    }
  • 相关阅读:
    failed to push some refs to 'git@github.com:cq1415583094/MyBatis.git'解决办法
    MyBatis 安装和配置
    MyBatis入门
    LinkedList 源码分析
    ArrayList 源码分析
    什么是注解?
    什么是泛型?
    什么是反射?
    php针对各数据库系统对应的扩展
    DedeCMS文章标题长度最全修改方法
  • 原文地址:https://www.cnblogs.com/shuigu/p/6351297.html
Copyright © 2020-2023  润新知