KVO的使用二:常用方法及小技巧

        (文章及代码接上一篇)

options详解:

        KVO的注册方法中有一个options枚举,用来确定观察者的接收消息方法接收的信息,那么具体有什么关联呢?下面通过一段代码来验证是如何关联的。依次选择options的枚举值,在接收消息方法中打印,如下:

NSKeyValueObservingOptionNew:

    NSKeyValueObservingOptionOld:

    NSKeyValueObservingOptionInitial

    

    NSKeyValueObservingOptionPrior:

   

      上面的打印结果表明,NSKeyValueObservingOptionNew的接收消息方法change字典中包含新值和kind,NSKeyValueObservingOptionOld是旧值和kind,NSKeyValueObservingOptionInitial的打印结果中有两行信息(观察者注册都有,其他的图片上未截取),第一条是观察者注册时接收的,第二条是在点击屏幕后,中间插入了一条Log可以看出通知是在注册方法未返回时就已被接收了。NSKeyValueObservingOptionPrior也有两条打印信息,这个是在name改变前后都会各自发送一次通知,第一条的notificationIsPrior表明是在change前发出的,后面的是在change后发出的。后面的两种情况都是不带有改变前后的键值,但是kind都存在。

        打印结果中的new、old其实就是苹果封装的const string,根据下图很好对应,取值用objectForKey:@“new”和objectForKey:NSKeyValueChangeNewKey效果是一样的,看自己喜好来(可能系统哪天溅嗖嗖的把const的值变一下,用new就会取不到了,?微软的德行最喜欢改API,苹果还好点)。NSKeyValueChangeIndexesKey这个是针对NSArray的,用来确定改变的对象。

           

        options的枚举值可以并用,当选择多个枚举值时,结果中就会包含多个选项。比如同时使用NSKeyValueObservingOptionNewNSKeyValueObservingOptionOld

       

        只是我没弄明白这个kind是代表什么,有知道希望赐教下。

 

context小技巧:

       作为用来区分消息源的值,我经常的做法是用一个字符串或一个常量去标识它,这样简单方便。比如针对上文的情况,使用const NSString *PersonName = @"PersonName"来作为name的标识,这样是可行的一种方法。在看了苹果的标识方法后,发现用一个const nsstring的弊端:对于一个人或者需要标识量少的情况下这种方法可行,但是在标识量大或多人开发的项目中,尤其是Person存在继承关系,难免不发生常量名、常量值一样的情况,这种情况很容易混淆要监听的键值。而苹果的做法确实极高一筹,它用一个地址static void *PersonNameContext = &PersonNameContext当作标识,这样就能确定唯一性。

 

willChangeValueForKey与didChangeValueForKey:

这两个方法可以和接收消息方法搭配使用,需要在被观察者的.m文件中重写。我刚重写这两个方法后,只添加了一句NS Log来打印信息,结果消息接收方法不响应了,找了半下午终于发现原因:需要调用父类该方法详情见官方文档。大多数情况下我们用不到这两个方法,苹果介绍里写的是在手动实现KVO时要实现这两个方法。

 

调用位置在set方法里,下文是KVO语法指导里的例子:

-(void)setBalance:(double)theBalance {

    [self willChangeValueForKey:@"balance"];

    _balance = theBalance;

    [self didChangeValueForKey:@"balance"];

         使用方法如上,它与消息接收方法的响应顺序如下:

         先注册,设置键值后响应willChangeValueForKey方法,接着是消息接收方法,最后是didChangeValueForKey;对于option是NSKeyValueObservingOptionPrior的情况,在willChangeValueForKey方法之前会先响应预通知。

 

手动通知:

苹果提供了两种通知方式:自动通知与手动通知。上面的例子是自动通知,当注册完成后,只要改变了注册的键值,观察者就会收到通知;但有时候一个观察者可能要观察好多键,比如一个父类中监听一个键值子类却不需要,这样的话子类就不需要接收这个键值改变的消息,这时可以通过手动通知屏蔽掉该属性。

被观察者类实现如下方法:

key是被注册的键,当注册成功后,该方法会立即被调用。

+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {

    BOOL automatic = NO;

    if ([key isEqualToString:@"name"]) {

        automatic = NO;

    }

    else {

        automatic = [super automaticallyNotifiesObserversForKey:key];

    }

    

    return automatic;

}

通过这种方式,可以灵活的在自动KVO和手动KVO之间切换。

⚠️手动KVO时,别忘了调用willChangeValueForKeydidChangeValueForKey两个方法,顺序和上文的例子一样。

 

依赖键:

      有些键的值由一个对象的一个或多个值来决定的,这时就可以注册一个依赖键来处理这个问题。当然也可以在决定的属性的set方法里进行操作,但是这样使set方法显得冗杂。

      比如外国人名字用fullName表示,fullName是由firstName和lastName决定的,当你改变两者中的一个或全部,fullName也跟随着改变,这种情况用正常的KVO实现肯定是不好用的,因为没有直接改变fullName的值,下面验证下:

      首先在fullName的get方法里关联firstName和lastName(注册方法的keyPath不要忘记改为fullName):

      - (NSString *)fullName {

          return [NSString stringWithFormat:@"%@ %@", _firstName, _lastName];

      }

       然后改变firstName的值:

      - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

          self.person.firstName = @"Jack";

     }

      观察打印结果,和预测的结果一样,没有响应即没有调用接收消息方法:

      苹果提供了两种方法供我们使用,keyPathsForValuesAffectingValueForKey和keyPathsForValuesAffecting<Key>。通过名  字可以推测出第一个方法是针对所有键的,要在其方法内部判断键后进行设置;后一个方法是为单独的键设计的,每一个键都有这个方法。着两个方法返回的都是一个NSSet类型的数据,通过这个实现了对fullName的间接KVO。

         keyPathsForValuesAffectingValueForKey方法:

        + (NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key {

             NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];

             if ([key isEqualToString:@"fullName"]) {

             NSArray *affectingKeys = @[@"lastName", @"firstName"];

             keyPaths = [keyPaths setByAddingObjectsFromArray:affectingKeys];

             }

           return keyPaths;

       }

       keyPathsForValuesAffecting<Key>方法:

      + (NSSet<NSString *> *)keyPathsForValuesAffectingFullName {

          return [NSSet setWithObjects:@"lastname", @"firstName", nil];

      }

依赖键分为To-One Relationships和To-Many Relationships两种情形,上面的是To-One,To—Many的情况主要用CoreData实现,比较复杂,感觉实用性也不大,就不介绍了。

 

转载于:https://www.cnblogs.com/xuanyishare/p/9330567.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值