博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
KVC和KVO(一)
阅读量:6180 次
发布时间:2019-06-21

本文共 5553 字,大约阅读时间需要 18 分钟。

欢迎大家关注我的公众号,我会定期分享一些我在项目中遇到问题的解决办法和一些iOS实用的技巧,现阶段主要是整理出一些基础的知识记录下来

文章也会同步更新到我的博客:

观察model对象的变化

在 Cocoa 的模型-视图-控制器 (Model-view-controller)架构里,控制器负责让视图和模型同步。这一共有两步:当 model 对象改变的时候,视图应该随之改变以反映模型的变化;当用户和控制器交互的时候,模型也应该做出相应的改变。

KVO 能帮助我们让视图和模型保持同步。控制器可以观察视图依赖的属性变化。

让我们看一个例子:我们的模型类 LabColor 代表一种 Lab色彩空间里的颜色。和 RGB 不同,这种色彩空间有三个元素 L, a, b。我们要做一个用来改变这些值的滑块和一个显示颜色的方块区域。

我们的模型类有以下三个用来代表颜色的属性:

@property (nonatomic) double lComponent;@property (nonatomic) double aComponent;@property (nonatomic) double bComponent;

依赖的属性

我们需要从这个类创建一个 UIColor 对象来显示出颜色。我们添加三个额外的属性,分别对应 R, G, B:

@property (nonatomic, readonly) double redComponent;@property (nonatomic, readonly) double greenComponent;@property (nonatomic, readonly) double blueComponent;@property (nonatomic, strong, readonly) UIColor *color;

有了这些以后,我们就可以创建这个类的接口了:

@interface LabColor : NSObject@property (nonatomic) double lComponent;@property (nonatomic) double aComponent;@property (nonatomic) double bComponent;@property (nonatomic, readonly) double redComponent;@property (nonatomic, readonly) double greenComponent;@property (nonatomic, readonly) double blueComponent;@property (nonatomic, strong, readonly) UIColor *color;@end

维基百科提供了转换 RGB 到 Lab 色彩空间的算法。写成方法之后如下所示:

- (double)greenComponent;{    return D65TristimulusValues[1] * inverseF(1./116. * (self.lComponent + 16) + 1./500. * self.aComponent);}[...]- (UIColor *)color{    return [UIColor colorWithRed:self.redComponent * 0.01 green:self.greenComponent * 0.01 blue:self.blueComponent * 0.01 alpha:1.];}

这些代码没什么令人激动的地方。有趣的是 greenComponent 属性依赖于 lComponent 和 aComponent。不论何时设置 lComponent 的值,我们需要让 RGB 三个 component 中与其相关的成员以及 color 属性都要得到通知以保持一致。这一点这在 KVO 中很重要。

Foundation 框架提供的表示属性依赖的机制如下:

+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key

更详细的如下:

+ (NSSet *)keyPathsForValuesAffecting
<键名>

在我们的例子中如下:

+ (NSSet *)keyPathsForValuesAffectingRedComponent{    return [NSSet setWithObject:@"lComponent"];}+ (NSSet *)keyPathsForValuesAffectingGreenComponent{    return [NSSet setWithObjects:@"lComponent", @"aComponent", nil];}+ (NSSet *)keyPathsForValuesAffectingBlueComponent{    return [NSSet setWithObjects:@"lComponent", @"bComponent", nil];}+ (NSSet *)keyPathsForValuesAffectingColor{    return [NSSet setWithObjects:@"redComponent", @"greenComponent", @"blueComponent", nil];}

注意

这里解释一下,可能有一些朋友对这里的对象依赖关系不是特别清楚。我们拿出一个来解释

+ (NSSet *)keyPathsForValuesAffectingGreenComponent{    return [NSSet setWithObjects:@"lComponent", @"aComponent", nil];}

重要

在这个方法中,它代表的意思是当lComponent属性,或者aComponent属性改变时,需要通知到greenComponent,就相当于,lComponent或者aComponent中的任何一个作出改变时,可以认为greenComponent也发生了改变,如果这时,有一个监听对象,在监听greenComponent,那么当我们改变lComponent或aComponent,这个监听会被触发

至于这个方法怎么得来的,在peoperty中定义了该属性,你只要敲keyPath就会自动提示出来,有哪些可以设置依赖了

在我们的color中

+ (NSSet *)keyPathsForValuesAffectingColor{    return [NSSet setWithObjects:@"redComponent", @"greenComponent", @"blueComponent", nil];}

color是依赖于redComponent、greenComponent和blueComponent,这样 我们就能知道,改变lComponent、aComponent、bComponent任何一个,都会触发color的监听

现在我们完整的表达了属性之间的依赖关系。请注意,我们可以把这些属性链接起来。打个比方,如果我们写一个子类去 override redComponent 方法,这些依赖关系仍然能正常工作。

观察变化

现在让我们目光转向控制器。 UIViewController 的子类拥有 LabColor model 对象作为其属性。

@interface ViewController ()@property (nonatomic, strong) LabColor *labColor;@end

我们把视图控制器注册为观察者来接收 KVO 的通知,这可以用以下 NSObject 的方法来实现:

- (void)addObserver:(NSObject *)anObserver         forKeyPath:(NSString *)keyPath            options:(NSKeyValueObservingOptions)options            context:(void *)context

这会让以下方法:

- (void)observeValueForKeyPath:(NSString *)keyPath                      ofObject:(id)object                        change:(NSDictionary *)change                       context:(void *)context

在当 keyPath 的值改变的时候在观察者 anObserver 上面被调用。这个 API 看起来有一点吓人。更糟糕的是,我们还得记得调用以下的方法

- (void)removeObserver:(NSObject *)anObserver            forKeyPath:(NSString *)keyPath

来移除观察者,否则我们我们的 app 会因为某些奇怪的原因崩溃。

对于大多数的应用来说,KVO 可以通过辅助类用一种更简单优雅的方式实现。我们在视图控制器添加以下的观察记号(Observation token)属性:

@property (nonatomic, strong) id colorObserveToken;

当 labColor 在视图控制器中被设置时,我们只要 override labColor 的 setter 方法就行了:

- (void)setLabColor:(LabColor *)labColor{    _labColor = labColor;    self.colorObserveToken = [KeyValueObserver observeObject:labColor                                                     keyPath:@"color"                                                      target:self                                                    selector:@selector(colorDidChange:)                                                     options:NSKeyValueObservingOptionInitial];}- (void)colorDidChange:(NSDictionary *)change;{    self.colorView.backgroundColor = self.labColor.color;}

我们封装一个KeyValueObserver辅助类

KeyValueObserver 辅助类 封装了 -addObserver:forKeyPath:options:context:,-observeValueForKeyPath:ofObject:change:context:和-removeObserverForKeyPath: 的调用,让视图控制器远离杂乱的代码。

// 其中__attribute__((warn_unused_result))这个的意思是,当调用这个方法时,必须要检查返回值,或者使用返回值,不然编译器直接报警告+ (NSObject *)observeObject:(id)object keyPath:(NSString*)keyPath target:(id)target selector:(SEL)selector __attribute__((warn_unused_result));+ (NSObject *)observeObject:(id)object keyPath:(NSString*)keyPath target:(id)target selector:(SEL)selector options:(NSKeyValueObservingOptions)options __attribute__((warn_unused_result));

整合到一起

视图控制器需要对 L,a,b 的滑块控制做出反应:

- (void)updateLComponent:(UISlider *)sender;{    self.labColor.lComponent = sender.value;}- (void)updateAComponent:(UISlider *)sender;{    self.labColor.aComponent = sender.value;}- (void)updateBComponent:(UISlider *)sender;{    self.labColor.bComponent = sender.value;}

我们来看一下效果

源工程:

和KVO(一)

参考:

https://www.objccn.io/issue-7-3/

转载地址:http://dndda.baihongyu.com/

你可能感兴趣的文章
8月不支持 64 位,App 将无法上架 Google Play!需要怎么做?
查看>>
Vs - 基于 d3.js 和 vue.js 的数据可视化
查看>>
优雅地使用loading
查看>>
Node8.0 之 Napi 探秘
查看>>
TypeScript入坑
查看>>
(三)spring cloud微服务分布式云架构-服务网关zuul初级篇
查看>>
Spring Cloud--Honghu Cloud分布式微服务云系统—System系统管理
查看>>
Linux服务器配置——SAMBA
查看>>
我的WP7应用
查看>>
js打开连接 _无需整理
查看>>
我的友情链接
查看>>
C语言结合windowsApi遍历文件
查看>>
linux 系统无法启动的基本解决方法
查看>>
Yii框架学习笔记 [PHP]
查看>>
饿了么MySQL异地多活的数据双向复制经验谈
查看>>
MySQL的btree索引和hash索引的区别
查看>>
计算机基础
查看>>
我的友情链接
查看>>
Hystrix系列-4-Hystrix的动态配置
查看>>
oracle数字函数
查看>>