iOS:KVO 实现观察者模式

文章结构

前言

在 iOS 开发中,常常需要在不同的对象、不同的视图(View)或不同的视图控制器(ViewController)之间通信,传递数据。主要的实现方法有:

  • 直接通过 superView 或 subView 传递数据,或者在类中添加其他对象的引用。方法直接但效率低、容易使代码混乱,难以处理复杂的关系。
  • 通过自带的或自定义的 delegate 协议通信。效率较高,能完成复杂的通信及执行复杂的操作,代码结构较好,但是代码量比较大。
  • 使用 KVO(Key-value observing)。能够穿越复杂的关系网,直接观察其他对象的属性,获取信息,根据所观察对象的变化进行响应,代码量少。

这些方法各有优劣,在不同的情况下选用合适的方法是最好的。因此掌握这些方法,才能更好地应对各种开发难题。KVO 是本文关注的重点。

KVO 简介

在 Apple 的应用开发里 KVO 提供了一个途径,使对象(观察者)能够观察其他对象(被观察者)的属性,当被观察者的属性发生变化时,观察者就会被告知该变化。这其实就对应设计模式中的观察者模式。

观察者:Observer,the observing object;被观察者:the observed object

前提条件

在实现 KVO 之前,需要确保被观察的对象是支持 KVO 的。通常继承自 NSObject 的对象都会自动支持 KVO。对于非继承自 NSObject 的类,也可以手动实现 KVO 支持

适用场景

KVO 能很方便地实现模型(Model)和控制器(Controller)之间的通信。主要的应用场景有:

KVO 能够实现一对多、多对多、多对一的观察。也就是说,KVO 没有限制观察者和被观察者的数量。当同时观察多个对象时,不但对象本身发生改变时会告知观察者,而且被观察对象发生替换、删除或插入等操作时也会告知观察者。

实现 KVO:注册观察者和观察者方法

基本流程

  1. 添加观察者:addObserver:forKeyPath:options:context:
  2. 实现观察响应方法:observeValueForKeyPath:ofObject:change:context:
  3. 在观察者 deallocted 之前移除观察者: removeObserver:forKeyPath:

添加观察者

observedObject.addObserver:forKeyPath:options:context:

注意:

  • 调用该函数的 observedObject 是被观察者,参数 addObserver 后面的是观察者;
  • forKeyPath 参数是 String 类型的,代表 observedObject 的属性,私有属性也可以观察,但是在 Swift 中需要把被观察对象的属性用 dynamic 标记,如:
class ObservedObjectClass: NSObject {
//在 Swift 中要用 dynamic 标记被观察的属性
    dynamic private var observedProperty = ""
    ...
}
  • options:可以选择获取的数据包含哪些内容,获取的数据是以字典的形式传递的。

    • NSKeyValueObservingOptionOld: 获取变化前的数据
    • NSKeyValueObservingOptionNew: 获取变化后的数据
    • NSKeyValueObservingOptionInitial: 获取设置观察者时被观察者的初始数据,即在 addObserver 函数调用完成前,被观察者的数据。
    • NSKeyValueObservingOptionPrior: 在变化前后分别发送消息(共发送两次消息)
  • context:可选的参数,会随着观察消息传递,用于区分接收该消息的观察者。一般情况下,只需通过 keyPath 就可以判断接收消息的观察者。但是当父类子类都观察了同一个 keyPath 时,仅靠 keyPath 就无法判断消息该传给子类,还是传给父类。

  • addObserver 并不会维持对观察者、被观察者和 Context 的强引用。如果需要的话,要自行维持对它们的强引用。

观察响应方法:

所有的观察者都必须实现观察响应方法:
observeValueForKeyPath:ofObject:change:context:

  • change 是一个字典,包含了一系列键-值。

    • NSKeyValueChangeKindKey: 变化的类型
    • NSKeyValueChangeOldKey: 变化前的值
    • NSKeyValueChangeNewKey: 变化后的值
    • NSKeyValueChangeIndexesKey: 在所有变化中的坐标
  • NSKeyValueChangeKindKey 又包含了:

    • NSKeyValueChangeSetting
    • NSKeyValueChangeInsertion
    • NSKeyValueChangeRemoval
    • NSKeyValueChangeReplacement
  • 如果接收到的观察者消息于当前的 Context 不符,就需要把消息传给 父类,直到寻找到对应的 Context。

  • 如果一个消息传到了 NSObject 仍然没有找到它的观察者,那么就会抛出异常:NSInternalInconsistencyException。

移除观察者

当一个对象不再需要观察另一个对象时,就需要移除观察。

observedObject.removeObserver:forKeyPath:context:

这个方法和添加观察者的方法是对应的。

移除观察者需要注意以下几点:

  • 一个对象如果没有注册成为观察者,那么当调用 removeObserver 移除它时,就会抛出异常。所以想要安全地移除观察者,可以使用 do、try、catch 来调用 removeObserver。
  • 观察者不会在 dealloc 的时候自动移除。因此最晚必须在观察者 dealloc 时移除它。
  • 系统没有自带的方法用于判断一个对象是否注册为观察者,因此尽量在初始化的时候注册观察者,在 dealloc 时移除。

总结

KVO 能够在复杂的关系网中直接观察某个对象,合理的使用 KVO 能够简化代码。但是 KVO 也有很多坑,稍有不慎就会抛出异常或者无法建立观察。在实践中,还是应该选择合适的方法来完成对象间的通信,熟练应对各种情况。

欢迎访问我的Github:LinShiwei (Lin Shiwei) · GitHub

有任何疑问的话,欢迎在下方评论区讨论。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容