首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >KVO 在 iOS开发中的应用

KVO 在 iOS开发中的应用

作者头像
keyle
发布2024-11-01 12:10:58
发布2024-11-01 12:10:58
9230
举报
文章被收录于专栏:礼拜八不工作礼拜八不工作

如果我更改了一个属性的值,KVO会帮助我们检测这个值的变化,从而通知我们这个值改变了。典型的观察者模式。当然我想起在UNITY3D中,如果检视面板的数值发生改变对应的GameObject发生位移之类的。我曾经实现了一个inspector的编辑器类,值改变直接通知(其实是调用)对应的函数。在iOS这里 一切发生的那么自然。不需要额外实现 这是iOS的特性之一 名曰:KVO 。

前情提要

上一篇 KVC 讲到需要遵循的几条几本规则在 KVO 中同样适用。

  • 骆驼命名法,不能数字开头
  • 不能包含空格
  • 键必须是ASCII编码的
  • 使用默认的get/set

上一章 只要是针对 object 的元素查询,调用,筛选。本文主要是讲述 KVO 监听(观察者模式) 属性值的变更。

添加/移除 一个监听

addObserver 函数签名如下

  • addObserver:监听的接受脚本的类
  • context:随便传入任何值都可以,最后取出来的时候需要强转
  • forKeyPath: 选择一个需要监听的属性
  • options:可选项
代码语言:javascript
复制
#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;
}

控制台输出如下:

代码语言:javascript
复制
2017-03-03 15:17:22.085142 oc[52721:6279405] helloworld
2017-03-03 15:17:22.085371 oc[52721:6279405] Value Changed : stringOnBar
Program ended with exit code: 0

关于 options 可选项

上面的代码中 options 是个可选项,这里不需要处理所以填写0,详细的选项如下:

代码语言:javascript
复制
NSKeyValueObservingOptionNew     把更改之前的值提供给处理方法
NSKeyValueObservingOptionOld     把更改之后的值提供给处理方法
NSKeyValueObservingOptionInitial 把初始化的值提供给处理方法,一旦注册,立马就会调用一次。通常它会带有新值,而不会带有旧值。
NSKeyValueObservingOptionPrior   分2次调用。在值改变之前和值改变之后。
0                                不带任何参数进去

传递给监听的值在接受函数ofObject:(id)object可以获取到。

关于监听脚本

不用担心监听脚本过于复杂。其实是自动生成的 键入 observeValueForKeyPath 则会生成如下代码:

代码语言:javascript
复制
- (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 的时候需要手动移除监听,否则会报错 … 报错信息如下:

代码语言:javascript
复制
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的通知,需要重写如下方法:

代码语言:javascript
复制
NSInteger HP = 10;
+(BOOL)automaticallyNotifiesObserversOfStringOnFoo
{
    if(HP>100)
      return YES;
    return NO;
}

这里有一个技巧输入 +(BOOL)automatically 之后自动列出可以重写的几个属性的通知。

手动触发KVO也是允许的,下面主要使用到了 willChangeValueForKey / didChangeValueForKey 这两个函数。即使禁止了自动通知也可以直接在get函数中触发。

代码语言:javascript
复制
NSInteger HP = 11;
+(BOOL)automaticallyNotifiesObserversOfStringOnFoo
{
    if(HP>100)
      return YES;
    return NO;
}
-(void)setStringOnFoo:(NSString *) inValue
{
    [self willChangeValueForKey:@"stringOnFoo"];
    stringOnFoo = inValue;
    [self didChangeValueForKey:@"stringOnFoo"];
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-03-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 礼拜八不工作 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前情提要
  • 添加/移除 一个监听
    • 关于 options 可选项
    • 关于监听脚本
  • 手动KVO
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档