如果我更改了一个属性的值,KVO会帮助我们检测这个值的变化,从而通知我们这个值改变了。典型的观察者模式。当然我想起在UNITY3D中,如果检视面板的数值发生改变对应的GameObject发生位移之类的。我曾经实现了一个inspector的编辑器类,值改变直接通知(其实是调用)对应的函数。在iOS这里 一切发生的那么自然。不需要额外实现 这是iOS的特性之一 名曰:KVO 。
 
前情提要 上一篇 KVC 讲到需要遵循的几条几本规则在 KVO 中同样适用。
骆驼命名法,不能数字开头 
不能包含空格 
键必须是ASCII编码的 
使用默认的get/set 
 
上一章 只要是针对 object 的元素查询,调用,筛选。本文主要是讲述 KVO 监听(观察者模式) 属性值的变更。
添加/移除 一个监听 addObserver 函数签名如下
addObserver:监听的接受脚本的类 
context: 随便传入任何值都可以,最后取出来的时候需要强转 
forKeyPath: 选择一个需要监听的属性 
options: 可选项 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #import <Foundation/Foundation.h>  #import "Foo.h"  #import "Bar.h"  int  main(int  argc, const  char  * argv[]) {    Foo* foo = [[Foo alloc]init];     foo.bar = [[Bar alloc]init];     [foo.bar addObserver:foo forKeyPath:@"stringOnBar"  options:0  context: (void *)s];          foo.bar.stringOnBar = @"test" ;     [foo.bar removeObserver:foo forKeyPath:@"stringOnBar" ];     return  0 ; } 
控制台输出如下:
1 2 3 2017 -03 -03  15 :17 :22.085142  oc[52721 :6279405 ] helloworld2017 -03 -03  15 :17 :22.085371  oc[52721 :6279405 ] Value Changed : stringOnBarProgram ended with exit code: 0  
关于 options 可选项 上面的代码中 options 是个可选项,这里不需要处理所以填写0,详细的选项如下:
1 2 3 4 5 NSKeyValueObservingOptionNew      把更改之前的值提供给处理方法NSKeyValueObservingOptionOld      把更改之后的值提供给处理方法NSKeyValueObservingOptionInitial  把初始化的值提供给处理方法,一旦注册,立马就会调用一次。通常它会带有新值,而不会带有旧值。NSKeyValueObservingOptionPrior    分2 次调用。在值改变之前和值改变之后。0                                 不带任何参数进去
传递给监听的值在接受函数 ofObject:(id)object 可以获取到。
关于监听脚本 不用担心监听脚本过于复杂。其实是自动生成的 键入 observeValueForKeyPath 则会生成如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 - (void )observeValueForKeyPath:(NSString  *)keyPath ofObject:(id )object change:(NSDictionary  *)change context:(void  *)context {          NSString * content = (__bridge NSString *)context;          if  (content) {         NSLog (@"%@" ,content);     }          if  ([keyPath isEqualToString:@"stringOnBar" ]) {          NSLog (@"Value Changed : stringOnBar" );         return ;     }     if  ([keyPath isEqualToString:@"stringOnFoo" ]) {         NSLog (@"Value Changed : stringOnFoo" );         return ;     }          [super  observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } 
当然结构体里面的内容是我自己些的 😄
当类 dealloc 的时候需要手动移除监听,否则会报错 … 报错信息如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2017 -03 -03  15 :16 :02.560251  oc[52593 :6273946 ] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException' , reason: 'An instance 0x1004065e0 of class Bar was deallocated while key value observers were still registered with it. Current observation info: <NSKeyValueObservationInfo 0x100406b90> ( <NSKeyValueObservance 0x100407e80: Observer: 0x1004045e0, Key path: stringOnBar, Options: <New: NO, Old: NO, Prior: NO> Context: 0x1000030c0, Property: 0x100406ac0> )' *** First throw call stack: ( 	0    CoreFoundation                      0x00007fffa311fe7b  __exceptionPreprocess + 171  	1    libobjc.A.dylib                     0x00007fffb7d09cad  objc_exception_throw + 48  	2    CoreFoundation                      0x00007fffa319e99d  +[NSException  raise:format:] + 205  	3    Foundation                          0x00007fffa4aff6e4  NSKVODeallocate  + 293  	4    oc                                  0x000000010000278e  -[Foo .cxx_destruct] + 78  	5    libobjc.A.dylib                     0x00007fffb7d05686  _ZL27object_cxxDestructFromClassP11objc_objectP10objc_class + 127  	6    libobjc.A.dylib                     0x00007fffb7cfe0c6  objc_destructInstance + 92  	7    libobjc.A.dylib                     0x00007fffb7cfe059  object_dispose + 22  	8    oc                                  0x000000010000166e  main + 398  	9    libdyld.dylib                       0x00007fffb85ed255  start + 1  ) libc++abi.dylib: terminating with uncaught exception of type NSException  (lldb)  
手动KVO KVO不是万能的,有时候我们在某些条件下不希望接受到KVO的通知,需要重写如下方法:
1 2 3 4 5 6 7 NSInteger  HP = 10 ;+(BOOL )automaticallyNotifiesObserversOfStringOnFoo {     if (HP>100 )       return  YES ;     return  NO ; } 
这里有一个技巧输入 +(BOOL)automatically 之后自动列出可以重写的几个属性的通知。
手动触发KVO也是允许的,下面主要使用到了 willChangeValueForKey / didChangeValueForKey 这两个函数。即使禁止了自动通知也可以直接在get函数中触发。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 NSInteger  HP = 11 ;+(BOOL )automaticallyNotifiesObserversOfStringOnFoo {     if (HP>100 )       return  YES ;     return  NO ; } -(void )setStringOnFoo:(NSString  *) inValue {     [self  willChangeValueForKey:@"stringOnFoo" ];     stringOnFoo = inValue;     [self  didChangeValueForKey:@"stringOnFoo" ]; } 
本文标题: KVO in iOS
文章作者: Keyle
发布时间: 2017-03-02
最后更新: 2024-08-20
原始链接: https://vrast.cn/posts/d0d6deab/ 
版权声明: ©Keyle's Blog. 本站采用署名-非商业性使用-相同方式共享 4.0 国际进行许可