从事iOS10年老人,对KVO的总结!一文讲透彻!!

5,110 阅读17分钟

前言

学如逆水行舟,不进则退!共勉!!!

生命不止,学习不停。今天已经是十一月一号了,淘宝预售也早已开始。不知各位掘金的兄弟姐妹们开始剁手了吗。今天来掘金了,主要是给大家分享一下一位从事iOS七八年老人对KVO的一些经验总结。废话不多说,现在就给大家分享。可以点点赞点点关注支持一下。ps:因为我是翻译的英文,如有错误之处不准确,还请留言区批评指正。

kvo

1.kvo介绍

KVO是一种允许对象在其他对象的指定属性发生变化时被通知的机制。

重点:为了理解KVO,首先你得理解KVO的译码

2.KVO的大致过程

//键-值观察提供了⼀种机制,允许将对象更改通知其它对象的特定属性。是特别有⽤的模型和控制器层在应⽤程序之间的通信。(在OS X,控制器层绑定技术严重依赖键值观察。)控制器对象通常观察模型对象的性质,和⼀个视图对象观察通过控制器模型对象的属性。此外,然⽽,⼀个模型对象可以观察其他模型对象(通常是确定相关的值更改时),甚⾄本身(再次决定当⼀个依赖的价值变化)。

//你可以观察属性包括简单的属性,⼀个关系,和许多关系。许多关系的观察⼈⼠通知类型的改变,以及哪些对象参与变⾰。

//⼀个简单的例⼦说明了KVO如何在应⽤程序中⾮常有⽤。假设⼀个Person对象与⼀个帐户对象,代表这个⼈的银⾏储蓄账户。实例的⼈可能需要知道当帐户实例的某些⽅⾯的变化,如平衡,或利率。

image.png

//如果这些属性是公共账户的性质,⼈可以定期轮询账户发现变化,但这当然是低效的,往往不切实际。更好的⽅法是使⽤KVO,它类似于⼈收到⼀个中断发⽣变化时。

//使⽤KVO,⾸先你必须确保观察对象,帐号在这种情况下,KVO兼容。⼀般来说,如果你的对象继承NSObject和创建属性以通常的⽅式,你的对象及其属性将⾃动KVO兼容。也可以⼿动实现遵从性。KVO合规描述了⾃动和⼿动键值观察之间的区别,以及如何实现。

//接下来,您必须注册您的观察者,的⼈,观察到的实例,Account。Person发送⼀个addObserver:forKeyPath:选择:背景:Account信息,⼀旦为每个观察关键路径,命名本身作为观察者

image.png

//为了收到Account更改通知,⼈实现了observeValueForKeyPath:ofObject:改变:背景:⽅法,要求所有的观察者。帐户将发送此消息给⼈任何时候注册的⼀个关键路径的变化。⼈可以采取适当的⾏动基于更改通知

image.png

//最后,当它不再想要通知,⾄少前⼀致,Person实例必须取消发送消息removeObserver:forKeyPath:Account。

image.png

注册键值观察描述了注册的完整⽣命周期,键值观测接收,注销登记通知。

//KVO的主要好处是,您不必实现⾃⼰的计划,发送通知每次属性变更。其定义良好的基础设施框架级⽀持,便于adopt-typically你不需要任何代码添加到您的项⽬。此外,基础设施已经全功能的,这使得它很容易为⼀个属性⽀持多个观察者,以及相关的值。

/注册相关的键解释了如何指定⼀个关键的价值依赖于另⼀个关键的价值

//与使⽤NSNotificationCenter的通知,没有中央对象为所有观察者提供了更改通知。相反,直接通知被发送到观察对象所做的修改。NSObject提供此基本实现键值的观察,你应该很少需要覆盖这些⽅法。

//键-值观察实现细节描述如何实现键值观察。

3.KVO的注册KVO

//你必须执⾏以下步骤启⽤对象为KVO-compliant属性接收键值观察通知:

  • 注册观察者与观察对象使⽤⽅法addObserver:forKeyPath:。
  • //实现observeValueForKeyPath:ofObject:改变:上下⽂:在观察者接受更改通知消息
  • //注销观察者使⽤⽅法removeObserver:forKeyPath:当它不再应该接收消息。⾄少,调⽤这个⽅法之前,观察者从内存中释放。。

//重要:并不是所有的类都是KVO-compliant所有属性。你能保证⾃⼰的类是KVO-compliant遵循KVO合规中描述的步骤。通常在Apple-supplied属性框架只是KVO-compliant如果他们记录。

4.KVO的注册观察者

//⼀个观察对象⾸先寄存器本身与观察对象发送⼀个addObserver:forKeyPath:选择:背景:消息,传递⾃⼰的观察者和关键路径属性被观察到。观察者另外指定⼀个选项参数和上下⽂指针管理⽅⾯的通知。

Options(KVO的枚举)

//选项参数,指定为⼀个常量按位或选项,将同时影响改变字典的内容提供通知,并⽣成通知的⽅式。你选择接受前观察到的属性的值改变NSKeyValueObservingOptionOld通过指定选项。你请求与选项NSKeyValueObservingOptionNew属性的新值。你收到新旧价值观的按位或这些选项。

//你指导观察对象发送⽴即更改通知(之前addObserver:forKeyPath:选择:背景:返回)NSKeyValueObservingOptionInitial的选项。您可以使⽤这些额外的,⼀次性通知在观察者建⽴属性的初始值。

//你指导观察对象发送⼀个通知之前属性改变(除了通常的通知后变化),包括选择NSKeyValueObservingOptionPrior。改变字典代表了包括关键NSKeyValueChangeNotificationIsPriorKey prechange通知NSNumber包装的价值肯定的。关键是不存在。时您可以使⽤prechange通知观察者的KVO合规需要调⽤⼀个这么…它的⼀个属性,⽅法取决于⼀个观察到的属性。通常的记账的调⽤将通知来得太晚了…。

Context (KVO的上下⽂)

//上下⽂指针addObserver:forKeyPath:选择:背景:消息包含任意数据将被传递回观察者在相应的更改通知。您可以指定NULL和完全依赖的关键路径字符串确定更改通知的起源,但是这种⽅法可能会导致问题的超类的⼀个对象也观察相同的关键路径是出于不同的原因。

//⼀个更安全、更可扩展的⽅法是使⽤上下⽂来确保通知你收到你的观察者,⽽不是⼀个超类。

//惟⼀命名的静态变量的地址在您的类上下⽂。上下⽂选择以类似的⽅式在超级-或⼦类可能重叠。你可以选择⼀个为整个类和上下⽂依赖的关键路径字符串的通知消息来确定发⽣了变化。或者,您可能会创建⼀个不同的上下⽂为每个观察关键路径,绕过需要字符串⽐较完全,导致更有效的通知解析。清单1显示了示例环境平衡和贷款利率的属性选择这种⽅式。

  • 清单1创建上下文指针:

image.png

//清单2中的示例演示了如何⼀个⼈实例寄存器本身为⼀个帐户实例作为观察者的资产和贷款利率属性使⽤给定的上下⽂指针。

//清单2注册审查员作为观察者的平衡和利率的属性

image.png

//注意:键值观察addObserver:forKeyPath:选择:背景:⽅法不保持强劲观察对象的引⽤,观察对象,或上下⽂。你应该确保你保持强引⽤观察,观察,必要时对象和上下⽂

5.Receiving Notification of a Change (KVO的接收到通知变化)

//时观察到的值属性对象的变化,观察者接收⼀个observeValueForKeyPath:ofObject:改变:背景:消息。所有观察家都必须实现这个⽅法。

//观察对象提供了关键路径,引发了通知,本身为相关的对象,⼀个字典,其中包含的细节变化,和上下⽂指针,当观察者提供了注册这个关键路径。

//改变字典条⽬NSKeyValueChangeKindKey提供信息的类型发⽣变化。如果观察到的值对象发⽣了变化,NSKeyValueChangeSetting NSKeyValueChangeKindKey条⽬的回报。指定的选项取决于观察者注册时,改变字典中的NSKeyValueChangeOldKey和NSKeyValueChangeNewKey条⽬包含属性的值之前,和之后,改变。如果属性是⼀个对象,直接提供的值。如果属性是⼀个标量或C结构,包裹在⼀个价值NSValue对象(与键值编码)。

//如果观察到的属性是⼀个很多的关系,NSKeyValueChangeKindKey条⽬还指示对象的关系是否插⼊,删除,或返回NSKeyValueChangeInsertion所取代,NSKeyValueChangeRemoval,或NSKeyValueChangeReplacement,分别。

//改变NSKeyValueChangeIndexesKey字典条⽬是⼀个NSIndexSet对象指定索引的关系改变了。如果NSKeyValueObservingOptionNew或NSKeyValueObservingOptionOld作为选项指定注册观察者时,改变字典中的NSKeyValueChangeOldKey和NSKeyValueChangeNewKey条⽬数组包含相关对象的值之前,和之后,改变。清单3中的示例显示了observeValueForKeyPath:ofObject:改变:背景:实现⽇志的⼈观察者的新旧值属性平衡和贷款利率,如清单2注册。

Listing 3 Implementation of observeValueForKeyPath:ofObject:change:context:

image.png

//如果你指定⼀个空上下⽂当注册⼀个观察者,你⽐较通知的关键路径和关键路径你观察来确定发⽣了什么变化。如果您使⽤⼀个上下⽂所有观察到的关键路径,你第⼀次测试,对通知的上下⽂,并找到⼀个匹配,使⽤关键路径字符串⽐较来确定具体发⽣了变化。如果你提供了⼀个独特的背景下为每个关键路径,是本⽂所演示的,⼀系列的简单指针⽐较同时告诉你是否通知观察者,,如果是这样,关键路径改变了什么。

//在任何情况下,观察者observeValueForKeyPath应该调⽤超类的实现:ofObject:改变:背景:当它不能识别上下⽂(或在简单的情况下,任何的关键路径),因为这意味着⼀个超类注册通知。注意:如果⼀个通知传播类层次结构的顶部,NSObject抛出NSInternalInconsistencyException因为这是⼀个编程错误:没有⼀个⼦类使⽤它注册的通知。

6.Removing an Object as an Observer(KVO的移除观察者)

//你删除⼀个键-值观察者通过发送观察对象removeObserver:forKeyPath:背景:信息,指定观察对象,关键路径和上下⽂。清单4中的示例显示了⼈删除本身作为观察者的平衡和贷款利率。

清单4删除作为balance和interestRate观察者的检查器

image.png

//在收到removeObserver:forKeyPath:背景:消息,观察对象将不再接收任何observeValueForKeyPath:ofObject:改变:背景:消息指定关键路径和对象。

When removing an observer, keep several points in mind:(移除观察者注意事项)

  • //作为观察员要求删除如果不是NSRangeException已经注册为⼀个结果。你要么叫removeObserver:forKeyPath:背景:到底⼀次相应的调⽤addObserver:forKeyPath:选择:背景:,或如果在你的应⽤程序,这不是可⾏的地⽅removeObserver:forKeyPath:背景:在⼀个try / catch块来处理潜在的例外。

  • //⼀个观察者本身不会⾃动删除时收回。观察对象继续发送通知,⽆视的状态观测器。然⽽,更改通知,像其他任何信息,发送给发布对象,触发⼀个内存访问异常。因此确保观察员从内存中删除⾃⼰消失之前。

  • //协议提供了没有办法问⼀个对象是⼀个观察者或被观察到。构建你的代码,以避免释放相关的错误。典型的模式是登记为⼀个观察者在观察者的初始化(例如在init或viewDidLoad)和注销在回收dealloc(通常),确保正确配对和命令添加和删除消息,观察者是未注册前从内存中释放。

7:KVO Compliance (KVO需要注意的⼏点)

为了被认为是特定属性的kvo兼容,类必须确保以下几点:

  1. //类必须键值编码的属性,指定在确保现有的遵从性。

  2. //KVO⽀持与现有的相同的数据类型,包括objective - c对象和标量中列出的标量和结构和结构的⽀持。

  3. //类排放KVO属性更改通知。

4.//依赖键注册适当的(参⻅注册相关的键)。

//有两个技术确保发出的变更通知。⾃动默认NSObject和提供的⽀持是可⽤于类的所有属性键值编码兼容。通常,如果你遵循标准可可编码和命名约定,您可以使⽤⾃动改变⽤户不需要编写额外的代码。

//⼿动更改通知通知发出时,提供了额外的控制,需要额外的编码。你可以控制⾃动通知你的⼦类实现的属性类⽅法

automaticallyNotifiesObserversForKey:

8:Automatic Change Notification(⾃动观察通知)

//NSObject提供了⼀个基本实现⾃动键-值变化的通知。⾃动键-值更改通知通知观察者使⽤键值的读写⽅法的更改,以及键值编码⽅法。还⽀持⾃动通知返回的代理对象集合,例如,mutableArrayValueForKey:。

//示例如清单1所示的结果在任何观察员的属性名变更的通知。

Listing 1 Examples of method calls that cause KVO change notifications to be emitted image.png

9:Manual Change Notification(⼿动观察通知)

//在某些情况下,您可能希望通知过程的控制,例如,减少引发不必要的特定于应⽤程序的原因,通知或组的数量变化成⼀个通知。⼿动更改通知提供了⼿段。

//⼿动和⾃动通知并不是相互排斥的。你⾃由发⾏⼿册除了⾃动的通知已经到位。通常情况下,你可能想要完全控制通知特定属性。在这种情况下,您覆盖的NSObject实现automaticallyNotifiesObserversForKey:。属性的⾃动通知你想排除,automaticallyNotifiesObserversForKey的⼦类实现:应该没有回来。⼀个⼦类实现应该为任何未被调⽤超键。清单2中的示例允许⼿动通知资产产权,允许超类决定通知所有其他键。

清单2 automcallynotifiesobserversforkey的实现示例:

image.png

//实现⼿动通知观察者,你调⽤willChangeValueForKey:改变价值之前,和didChangeValueForKey:改变后的值。中的例⼦

清单3实现了balance属性的手动通知。

清单3实现手动通知的访问器方法示例

image.png

//发送不必要的通知可以最⼩化⾸先检查如果值已经改变了。清单4中的示例测试资产的价值,如果它改变了只提供通知

清单4在提供通知之前测试值的更改

image.png

如果单个操作导致多个键发生更改,则必须嵌套更改通知,如清单5所示。

清单5多个键的嵌套更改通知

image.png

//在⼀个有序的情况下许多关系,您必须指定不仅改变的关键,但也改变的类型和对象的索引。的变化是⼀个NSKeyValueChange类型指定NSKeyValueChangeInsertion,NSKeyValueChangeRemoval或NSKeyValueChangeReplacement。受影响对象的索引作为NSIndexSet传递对象。清单6中的代码⽚段演示了如何删除对象封装在许多关系事务。

Listing 6 Implementation of manual observer notification in a to-many relationship

image.png

10:Registering Dependent Keys (注册依赖keys)

//有许多情况下,⼀个属性的值取决于其他⼀个或多个属性的另⼀个对象。如果⼀个属性的值变化,那么派⽣属性的值也应该改变的标记。你如何确保键值观察通知发布这些依赖属性取决于关系的基数。

11:To-One Relationships (单⼀关系处理)

//⾃动触发通知你应该覆盖keyPathsForValuesAffectingValueForKey:⼀个关系或实现⼀个合适的⽅法,遵循模式定义注册相关的键。

//例如,⼀个⼈的全名是依赖于第⼀个和最后⼀个名称。⼀个⽅法,返回全名可以写成:

image.png

//应⽤程序观察fullName财产时,必须通知firstName和lastName属性的改变,因为他们影响房地产的价值。

//⼀个解决⽅案是覆盖keyPathsForValuesAffectingValueForKey:指定fullName属性依赖于⼈的名和姓的属性。清单1显示了⼀个示例实现的依赖:

Listing 1 Example implementation of keyPathsForValuesAffectingValueForKey:

image.png

//覆盖应该通常调⽤超级并返回⼀组包括的任何成员组,由于这样做(以免⼲扰覆盖超类的⽅法)。

//你也可以实现相同的结果通过实现⼀个类⽅法,遵循命名约定keyPathsForValuesAffecting <键>、<键>在哪⾥属性的名称(⾸字⺟⼤写),取决于价值观。清单1中的代码使⽤这个模式可以写成⼀个类⽅法命名keyPathsForValuesAffectingFullName如清单2所示。

Listing 2 Example implementation of the keyPathsForValuesAffecting naming convention

image.png

//你不能覆盖keyPathsForValuesAffectingValueForKey:⽅法当你将⼀个计算的属性添加到现有类使⽤⼀个类别,因为你不应该覆盖⽅法类别。在这种情况下,实现⼀个匹配keyPathsForValuesAffecting <键>类⽅法利⽤这种机制。

//注意:您不能设置许多依赖关系通过实现keyPathsForValuesAffectingValueForKey:。相反,您必须遵守适当的每个对象的属性很多收集和响应值的变化通过更新⾃⼰依赖的关键。以下部分显示了⼀个策略来处理这种情况。

12:To-Many Relationships (对于多种关系的处理)

//keyPathsForValuesAffectingValueForKey:不⽀持关键路径⽅法,包括许多关系。例如,假设您有⼀个使⽤许多部⻔对象关系(员⼯)员⼯,和员⼯⼯资属性。您可能希望部⻔对象有⼀个totalSalary属性依赖于所有的员⼯的⼯资关系。你不能这样做,例如,keyPathsForValuesAffectingTotalSalary和返回的员⼯。⼯资作为⼀个关键。

  1. 在这两种情况下都有两种可能的解决方案:

//您可以使⽤键值观察注册⽗(在这个例⼦中,部⻔)作为观察者的相关属性的所有的孩⼦(员⼯在本例中)。作为观察员必须添加和删除⽗在⼦对象添加和删除的关系(参⻅注册键值观察)。observeValueForKeyPath:ofObject:改变:背景:⽅法依赖价值变化⽽更新,如以下代码⽚段:

image.png

  1. 如果你使⽤核⼼数据,您可以注册⽗与应⽤程序的通知中⼼作为观察者的管理对象上下⽂。⽗⺟应该孩⼦应对相关变化通知发布的键值观察的⽅式类似。

13:Key-Value Observing Implementation Details(KVO实现的细节)

//⾃动键值观察使⽤⼀种叫做isa-swizzling的技术实现。isa指针,顾名思义,指向的对象的类维护调度表。这个调度表基本上包含指向类实现⽅法,以及其他数据。当⼀个观察者注册对象的⼀个属性isa观察对象的指针被修改,指着⼀个中间类⽽不是在真正的类。由于isa指针的值并不⼀定反映实际的类的实例。你不应该依靠isa指针来确定类会员。相反,您应该使⽤类⽅法来确定类对象的实例。

14.Document Revision History (⽂档版本)

该表描述了对Key-Value Observing Programming Guide的修改。

image.png

如果你觉得你有疑问之处,可以再评论区留言讨论。如需要原文的ppt都可以在下方获取,我也会给大家分享我近些年收集的一些iOS资料。人间有真情,iOS圈子有温暖。最后求一波赞和点赞。

ppt原文获取地址,iOS资料:下载地址