close
当前位置: 物联网在线 > 技术文库 > ios >

如何优雅地使用 KVO

KVO 作为 iOS 中一种强大并且有效的机制,为 iOS 开发者们提供了很多的便利;我们可以使用 KVO 来检测对象属性的变化、快速做出响应,这能够为我们在开发强交互、响应式应用以及实现视图和模型的双向绑定时提供大量的帮助。

但是在大多数情况下,除非遇到不用 KVO 无法解决的问题,笔者都会尽量避免它的使用,这并不是因为 KVO 有性能问题或者使用场景不多,总重要的原因是 KVO 的使用是在是太 ** 麻烦 了。

如何优雅地使用 KVO

使用 KVO 时,既需要进行 注册成为某个对象属性的观察者 ,还要在合适的时间点将自己 移除 ,再加上需要 覆写一个又臭又长的方法 ,并在方法里 判断这次是不是自己要观测的属性发生了变化 ,每次想用 KVO 解决一些问题的时候,作者的第一反应就是头疼,这篇文章会为各位为 KVO 所苦的开发者提供一种更优雅的解决方案。

使用 KVO

不过在介绍如何优雅地使用 KVO 之前,我们先来回忆一下,在通常情况下,我们是如何使用 KVO 进行键值观测的。

首先,我们有一个 Fizz 类,其中包含一个 number 属性,它在初始化时会自动被赋值为 @0 :

// Fizz.h @interface Fizz : NSObject @property (nonatomic, strong) NSNumber *number; @end // Fizz.m @implementation Fizz - (instancetype)init { if (self = [super init]) { _number = @0; } return self; } @end

我们想在 Fizz 对象中的 number 对象发生改变时获得通知得到 的和 的值,这时我们就要祭出 -addObserver:forKeyPath:options:context 方法来监控 number 属性的变化:

Fizz *fizz = [[Fizz alloc] init]; [fizz addObserver:self forKeyPath:@"number" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil]; fizz.number = @2;

在将当前对象 self 注册成为 fizz 的观察者之后,我们需要在当前对象中覆写 -observeValueForKeyPath:ofObject:change:context: 方法:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context { if ([keyPath isEqualToString:@"number"]) { NSLog(@"%@", change); } }

在大多数情况下我们只需要对比 keyPath 的值,就可以知道我们到底监控的是哪个对象,但是在更复杂的业务场景下,使用 context 上下文以及其它辅助手段才能够帮助我们更加精准地确定被观测的对象。

但是当上述代码运行时,虽然可以成功打印出 change 字典,但是却会发生崩溃,你会在控制台中看到下面的内容:

2017-02-26 23:44:19.666 KVOTest[15888:513229] { kind = 1; new = 2; old = 0; } 2017-02-26 23:44:19.720 KVOTest[15888:513229] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'An instance 0x60800001dd20 of class Fizz was deallocated while key value observers were still registered with it. Current observation info: <NSKeyValueObservationInfo 0x60800003d320> ( <NSKeyValueObservance 0x608000057310: Observer: 0x7fa098f07590, Key path: number, Options: <New: YES, Old: YES, Prior: NO> Context: 0x0, Property: 0x608000057400> )'

这是因为 fizz 对象没有被其它对象引用,在脱离 viewDidLoad 作用于之后就被回收了,然而在 -dealloc 时,并没有移除观察者,所以会造成崩溃。

我们可以使用下面的代码来验证上面的结论是否正确:

// Fizz.h @interface Fizz : NSObject @property (nonatomic, strong) NSNumber *number; @property (nonatomic, weak) NSObject *observer; @end // Fizz.m @implementation Fizz - (instancetype)init { if (self = [super init]) { _number = @0; } return self; } - (void)dealloc { [self removeObserver:self.observer forKeyPath:@"number"]; } @end

在 Fizz 类的接口中添加一个 observer 弱引用来持有对象的观察者,并在对象 -dealloc 时将它移除,重新运行这段代码,就不会发生崩溃了。

如何优雅地使用 KVO

由于没有移除观察者导致崩溃使用 KVO 时经常会遇到的问题之一,解决办法其实有很多,我们在这里简单介绍一个,使用当前对象持有被观测的对象,并在当前对象 -dealloc 时,移除观察者:

- (void)viewDidLoad { [super viewDidLoad]; self.fizz = [[Fizz alloc] init]; [self.fizz addObserver:self forKeyPath:@"number" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil]; self.fizz.number = @2; } - (void)dealloc { [self.fizz removeObserver:self forKeyPath:@"number"]; }

这也是我们经常使用来避免崩溃的办法,但是在笔者看来也是非常的不优雅,除了上述的崩溃问题,使用 KVO 的过程也非常的别扭和痛苦:

需要手动 移除观察者 ,且移除观察者的 时机必须合适

注册观察者的代码和事件发生处的代码上下文不同, 传递上下文 是通过 void * 指针;

需要覆写 -observeValueForKeyPath:ofObject:change:context: 方法,比较麻烦;

在复杂的业务逻辑中,准确判断被观察者相对比较麻烦,有多个被观测的对象和属性时,需要在方法中写大量的 if 进行判断;

虽然上述几个问题并不影响 KVO 的使用,不过这也足够成为笔者尽量不使用 KVO 的理由了。

优雅地使用 KVO

如何优雅地解决上一节提出的几个问题呢?我们在这里只需要使用 Facebook 开源的 KVOController 框架就可以优雅地解决这些问题了。


(责任编辑:ioter)

用户喜欢...

云计算使用不断增长

究竟何为云计算?最简单的解释是,它是一种基于互联网的服务,提供对共享计算资源(例如存储、服务器、网络、应用、服务)的按需访问,还可将数据传输到连网的设备。使用这些服务,用...


直接数字合成器 (DDS) 的基础知识及其选择和使用方式

随着无线设备数量的激增,设计人员正在使用越来越复杂多变的波形,来满足应用在数据速率、干扰抑制、成本、封装和低功耗方面的要求。这些波形需要有稳定的射频源:可以根据需要进行调...


使用无刷DC驱动器设计的六种奇怪方法

我已经知道了两个绝对真理。首先,无刷直流电机驱动器很酷。每个人都喜欢旋转电机的快感,无刷直流电机通过高速,高功率和高效率来提升令人惊叹的因素。其次,工程师喜欢有创意。有时...


飞行时间传感器(ToF)如何工作?看看ToF 3D相机

飞行时间传感器如何确定距离?使用示例3D相机了解有关ToF相机的更多信息。 有三种领先的3D成像技术可以在手机和汽车中竞争空间。这些技术是立体成像,结构光投影和飞行时间(或ToF)相机...


如何快速连接到云端并向其直接发送物联网数据然后进行远程访问

许多开发人员都在开发需要连接到云端的物联网 (IoT) 项目,以便进行数据存储和远程访问,以及使用即时的数据分析服务。然而,开发人员很难找到最有效、安全的连接方法。 本文将介绍如何...


什么是物联网?物联网如何运作

最简单的物联网是一个智能设备网络 - 从你从牛奶到工业传感器时提醒你的冰箱 - 连接到互联网以便他们可以共享数据,但物联网远非IT部门面临的一个简单挑战。 什么是物联网? 对于许多公...


如何利用物联网保护我们的家园和城市免受气候变化的影响

过去十年中,自然灾害显着增加。极端天气事件,野火,地震,海啸以及干旱和极地涡旋等缓慢发生的灾害使许多美国家庭处于危险之中。阿拉巴马州最近的龙卷风爆发和2018年在加利福尼亚创...


思考物联网(IoT)如何对音乐产业产生影响!

许多人会认为音乐是他们日常生活中非常重要的一部分。它围绕着我们的生活,工作和娱乐,让我们思考物联网(IoT)如何对音乐产业产生影响! 在本文中,我们将在现场音乐会设置中探索物...


使用LDPC编码处理5G NR DL-SCH和UL-SCH传输块

该示例突出显示了用于5G NR下行链路和上行链路共享传输信道(DL-SCH和UL-SCH)的低密度奇偶校验(LDPC)编码链。 共享通道参数 该示例使用DL-SCH来描述处理,其也适用于UL-SCH。 选择在下行链路共...


带你看看毫米波雷达芯片发展趋势如何?

随着自动驾驶技术的发展,相关的毫米波雷达也得到了许多关注。毫米波雷达在自动驾驶领域,是与激光雷达LiDAR和摄像头一样极其重要的传感器。同时,我们将会看到,毫米波雷达除了在无人...