KVO和KVC迅速

  • 介绍:

程序的流程取决于我们在代码中使用的各种变量的值。 根据程序中变量的值,我们根据需要/要求导航执行流程。

例如,如果我们使用任何集合类型,则在修改或更改集合时很难执行逻辑。 即添加,删除或修改新项目时。 我们仍然可以通过多种方式管理这种情况。

其中之一是使用Notification-Centre,以便在属性值发生更改时通知我们。 但是,如果要检查代码中的许多属性,则这种方式(使用Notification Center)将导致我们输入大量代码,以调用我们需要检查的所有那些属性。 在这种情况下,另一种更好的方式是(也被Apple在其库中大量使用了)称为KVO (键值观察),它也与另一个强大的机制KVC (键值编码)直接相关。

注意:我们要观察更改的任何属性都必须是KeyValueCoding(KVC)投诉。

这两个KVO和KVC都提供了一种有效的方式来编写我们的代码。 现在开始使用KVC,然后开始使用KVO。

  • KVC:

KVC是一种编码形式,它允许您间接访问对象的属性,使用字符串而不是属性的访问器或直接访问变量来访问它们。 要启用这种机制,您的类必须遵守NSKeyValueCoding非正式协议。 (要么)

键值编码是一种机制,用于间接访问对象的属性,而不是通过调用访问器方法或通过实例变量直接访问它们,而是使用字符串来标识属性。

例如: 课程简介:NSObject {
var firstName:字符串
var lastName:字符串
var customProfile:个人资料
}

如果我们要在类init()或类文件中的某个位置分配上述声明的变量的值,通常我们喜欢以下内容:

self.firstName =“罗伯特”
self.lastName =“斯塔克”
使用KVC; 我们确实喜欢以下内容:
self.setValue: 键为“ firstName ”的 Robert //self.setValue: 任意 键: key / KeyPath
self.setValue: 键为“ lastk ”, Stark
要检索KVC属性的值,我们使用如下所示:
让robertLastName = self.value(forKey: lastName ”)

上面的KVC工作方式就像迅速使用Dictionary。 对?

在这里,在KVC中; 与其直接将值分配给属性 ,或者不使用对象的 #ter setter方法 ,我们只是简单地分配key / keyPaths。 因此,我们使用 键和值 ,这种技术称为 键值编码(KVC)。

注意: 有一个称为 NSKeyValueCoding非正式协议的协议 ,必须与 KVC一起使用 为了使用KVC&KVO,必须对此协议确认我们的课程 NSObject 确认该协议。 因此 ,在Foundation框架中定义并从NSObject继承的每个类都符合NSKeyValueCoding协议。

  • 什么是键和KeyPath?

键: 简单地,“ ”指定一个属性,即我们要 设置一个值或从中获取一个的 属性 因此,其 名称 与属性名称相同。

例如: self.setValue: 键为“ lastk ”的 Stark

的keyPath: 的keyPath 由以下子串与该点语法形成的,所以它不是一个单一的字/串。 键路径 表示 对象的 所有属性 直到达到所需的值/属性为止

例如:

var myProfile: 个人资料
self.setValue:“ Baratheon ”作为密钥:“ myProfile。 customProfile.lastName

KVC:

  • 在这里,我们将创建一个单视图应用程序,以与KVC&KVO一起使用。 这个项目有两个示例项目,但是我们在这里探索的这个项目与任何UI都不相关。 另一个是在文本更改时在ViewController的视图上更新一个UILable。 项目链接将在底部共享,因此您可以轻松地自己探索。
  • 首先,快速创建一个单视图应用程序
  • 要使用KVC,我们的类需要确认NSKeyValueCoding协议。 因此,通过确认NSObject,我们可以实现此步骤。 UIViewController已经对此NSObject进行了确认,因此我们可以从该协议中调用方法而无需进行任何设置。
  • 在Xcode中,在您的项目中,创建一个新的swift文件,名称为Children,NSObject为基类(Children继承自NSObject)。 然后在该类文件中,通过在声明的前面放置' @objc dynamic '关键字来声明两个名为' name'和' age'的属性 那么为什么我们需要这样做呢???

→快速地,有许多关键字/属性可以帮助进行编译,运行时规范,访问控制等。例如:@转义,@ available等。同样,它还定义了一系列声明修饰符,以修改属性的声明/班级成员。 例如,通过用' final'关键字标记类声明,我们通知编译器该类不能被子类化。 这使编译器可以进行许多优化以提高性能。 ' dynamic'也是我们快速使用的声明修饰符。

动态调度 ,是Objective-C的很酷的功能之一。 这只是意味着Objective-C运行时在运行时决定需要调用特定方法或函数的哪种实现。 例如,如果子类覆盖其超类的方法,则动态调度将确定需要调用该方法的哪种实现,子类的实现或父类的实现。 这是一个非常强大的概念。

Swift会尽可能使用Swift运行时。 结果是它可以进行许多优化。 虽然Objective-C仅依赖于动态调度,但是Swift仅在没有其他选择的情况下才选择动态调度。 如果编译器可以在编译时确定需要选择哪种方法的实现,则通过选择不使用动态分配,可以节省几纳秒的时间。

Swift运行时会尽可能选择其他选项,例如静态虚拟调度 ,而不是动态调度 。 这样做可以提高性能。静态和虚拟调度比动态调度快得多。 即使我们正在谈论纳秒,最终结果仍然是惊人的。 我们习惯的许多功能只有通过动态的Objective-C运行时才能实现,包括核心数据和键值观察。

动态调度

通过将' dynamic'声明修饰符应用于类的成员,您可以告诉编译器应使用动态分派来访问该成员。

通过在声明的前面加上' dynamic'关键字,该声明将隐含地标记为objc属性。 objc属性使声明在Objective-C中可用,这是由Objective-C运行时调度它的要求。

动态”声明修饰符只能用于类的成员。 结构和枚举不支持继承,这意味着运行时不必确定需要使用哪种实现。

因此要快速使用KVC和KVO,对于我们要在KVO中观察的属性,我们需要使用@objc dynamic关键字对其进行声明。

  • 现在,我们在Children类中具有两个属性,即在初始化方法中初始化/定义它们。
  • 现在,在ViewController类文件中,声明三个子实例,如下所示:

首先,我们使用' child1'对象。 在viewDidLoad()方法中,我们初始化child1对象,然后将值分配给它的属性。

如果您打印' child1'对象的属性,例如名称和年龄,您将得到分配的值。

现在,我们将使用KVC方法执行相同的操作。

在以上代码段中,在前几行中,我们使用setValue:forKey:方法将期望值设置为两个属性。 注意键串与属性名称相同。

接下来,我们执行完全相反的任务。 我们使用valueForKey:方法从属性中提取值,并将它们分配给两个局部变量。 然后,我们在控制台中打印这些值。 结果与上一个相同。

注意:如果我们提供与属性名称不同的键,则该应用程序将崩溃。 在编写符合KVC的代码时,务必非常小心,因此密钥字符串实际上必须与属性名称匹配,否则应用程序将崩溃。 直接处理属性时不是这种情况,因为没有机会对属性名称进行任何错误; 在这种情况下,编译器将引发错误,这将促使我们对其进行修复。

通过以上所有内容,我们设法了解了如何编写KVC样式的代码,以及如何使用键设置和获取值。 在下一部分中,我们将完成项目。

使用关键路径:

现在转到Children类,并添加以下属性。

@objc动态var孩子:孩子?

viewDidLoad方法中。 现在,添加以下行以初始化相关对象并分配其初始值:

在上面的代码段中,在前几行中,我们刚刚初始化了“ child2”对象及其“ child”属性。 接下来,我们将值设置为属性名称child2的 年龄 。 对于child2child属性,我们使用key-path来设置值。 仔细观察。 我们可以检索值以检查分配是否成功:取消注释打印语句。

现在,我们将看到如果孩子的孩子也有孩子该怎么办……

如果要检查值/结果,请使用上述代码段的打印语句。

到目前为止,我们学习了如何使用键和键路径编写KVC投诉代码。 接下来,我们将学习观察物业的价值变化:

KVO:

在这里,我们将看到应该采取什么措施才能跟踪属性的变化。 首先,让我向您介绍实现KVO所需的步骤:

1.要观察其属性的类必须符合KVO。 这意味着:

  • 根据我们在引言和上一节中已经看到的内容,该类必须符合KVC。
  • 该类必须能够自动或手动发送通知(稍后我们将详细介绍)。

2.将用于观察另一个类的属性的类设置为观察者

3.应该对观察类实现一个特殊方法,名为observValue(forKeyPath keyPath:String ?,对象:Any ?,更改:[NSKeyValueChangeKey:Any] ?,上下文:UnsafeMutableRawPointer?)

让我们一一看。 当我们要观察属性的变化时,最重要的是使我们的班级观察这些变化。 与临时通知( NSNotifications )差不多完成了此操作,但是使用了另一种方法。 此方法是addObserver(<#T ## observer:NSObject ## NSObject#>,forKeyPath:<#T ## String#>,选项:<#T ## NSKeyValueObservingOptions#>,上下文:<#T ## UnsafeMutableRawPointer ?#>)

在这里,我们观察到child1对象的名称年龄属性的值更改。 因此,在viewWillAppear()方法中,添加child1对象的观察者。

上述方法接受的参数是:

  • addObserver :这是观察类,通常是self对象。
  • forKeyPath :我想您可以理解它的用途。 它是您用作键或键路径的字符串,并且与您要观察的属性匹配。 请注意,您在此处指定单个键或键路径。
  • options :一组NSKeyValueObservingOptions值。
  • context :这是一个指针,可用作我们观察到的属性更改的唯一标识符。 通常将此设置为nilNULL 。 我们稍后会详细介绍。

现在,我们已经使我们的类能够观察上述两个属性的任何变化,我们必须实现observeValueForKeyPath:ofObject:change:context:方法。 它的实施是强制性的,它有一个很大的缺点。 这就是每次KVO更改都需要调用的事实,如果您观察到许多属性,则必须编写许多if语句,以便对每个属性采取适当的操作。 但是,这很容易忽略,因为KVO的好处大于此限制。

每当添加到观察者的属性值发生变化时,都会调用上述方法。 在这里,通过key-path参数,我们打印了名称和年龄属性的新值和旧值。

要测试此功能,请将UIButton的操作连接到viewController,然后在该操作方法中添加以下代码行:

超! 在为child1对象的名称年龄属性设置了新值之后,我们收到了通知,并且要求显示的消息显示在调试器上。 如您所见,字典中既包含先前值,也包含新值。

您可以从变更字典中提取所需的任何值(如果需要),但是最重要的是,通知属性更改非常容易。

在按钮操作方法中添加以下代码行并运行程序。

如果您观察到结果,我们会收到两个有关age属性更改的通知。 但这似乎令人困惑,因为即使我们知道每个通知所属的对象,但以编程方式我们也无法确定发送通知的对象。 因此,我们将如何面对这一问题,以及如何以编程方式对更改后的属性所属的对象有100%的把握?

回答上述问题是:我们会利用的addObserver上下文参数的(<#牛逼##观察员:NSObject的## NSObject的#>,forKeyPath:<#牛逼###字符串>选项:<# T ## NSKeyValueObservingOptions#>,上下文:<#T ## UnsafeMutableRawPointer?#>)方法。 我之前已经提到过,上下文的目的是唯一地标识属性的更改,因此它是我们可以使用的最佳工具。

在这里,我为child1和child2定义了两个上下文:

请注意,每个观察到的属性的上下文值都必须是全局变量,因为必须从addObserver…observeValueForKeyPath…方法都可以访问它。

现在,通过传递上下文参数来修改child1和child2中的代码以添加观察者方法。 然后,我们需要像下面那样修改observe方法:这样,我们可以在传递的上下文参数的帮助下识别已更改的属性。 因此,我们设法以编程方式指定每个更改的属性。

最后,在本章结尾之前,删除添加的观察者在某些时候也很重要。 没有关于应该在哪里进行操作的方法。 例如,在许多情况 ,处理收到的通知后,在observeValueForKeyPath:ofObject:change:context:中执行此操作将很有用。 在其他情况下,应在关闭视图控制器后执行此操作。 通常,这取决于您的应用程序结构。 在此示例中,我们将在viewWillDissapear:方法中执行此操作。 这是:对于child2, 我在上面的代码段中删除了观察者。

自动和手动通知

默认情况下,当您使用KVO进行观察时,每次属性更改时,系统都会发送一条通知。 这在大多数情况下都是合适的,但是有时候,我们不希望在发生更改后,而是在多个属性中进行了一系列更改之后或以后收到通知。 值得庆幸的是,iOS SDK为我们提供了一些非常方便的方法,使我们可以控制通知,因此我们可以在实际需要时手动发送它们。 在深入了解更多细节之前,我只能说,使用接下来要看到的方法不是强制性的。 相反,如果确实需要,则可以实施它。

现在,为了控制属性更改时发送的通知,您必须实现automaticNotifyObserverForKey: class方法。 它接受的参数是您需要控制其通知的属性键的字符串表示形式,它返回一个布尔值。 如果您不希望在更改观察到的属性的值之后发送通知,则该方法必须返回false。 在任何其他情况下,您都应让iOS决定有关通知的信息。

实际上,让我们假设当Children类的name属性更改时,我们不希望发布通知。 考虑到这一点,这是Children类中该方法的实现

else子句中,我们使用类调用相同的方法,以便让iOS处理我们在此处未明确添加的所有键,而返回的值是最后返回的值。

如果此时运行该应用程序,您将发现调试器上未出现有关名称更改的消息。 当然,这是我们想要的,因此我们设法实现了目标。 但是,我们真的做到了吗?

嗯,正如您所了解的那样,通过在上述方法中为特定密钥返回false,我们仅设法阻止了相关通知的发送。 当然,这并不意味着手动通知,而是根本没有通知! 为了在我们决定时发送通知,我们必须使用其他两种方法。 这些是willChangeValueForKey:didChangeValueForKey :。 使用它们时,必须先调用willChangeValueForKey : ,然后必须将新值分配给该属性,而在最后应调用didChangeValueForKey:。 要测试手动操作,请在viewcontroller的action方法中添加这些行。

如果您现在运行该应用程序,则有关名称更改的消息将显示在调试器上,这意味着我们已成功手动发送了通知!

实际上,通知是在didChangeValueForKey:方法被调用之后发送的,因此,将该方法放在您认为适合接收通知的位置。

这正是我们一直期望的应用程序! 如您所见,我们已经设法控制了通知发送点,而这一切都花了一点力气! 请注意,在willChangeValueForKey:didChangeValueForKey:方法之间,可以分配多个属性值。

并非必须强制使用willChangeValueForKey:didChangeValueForKey:方法,如上所示。 它们也可以在Children类(或您自己的类)中实现。

请单击此处查看上述教程的项目。

参考文献:

  1. http://michael-brown.net/2017/swift-and-kvo-context-variables/
  2. https://www.appcoda.com/understanding-key-value-observing-coding/
  3. https://cocoacasts.com/key-value-observing-kvo-and-swift-3
  4. https://www.ralfebert.de/ios-examples/swift/property-key-value-observer/
  5. https://blog.scottlogic.com/2015/02/11/swift-kvo-alternatives.html

如果您喜欢我的教程,请在媒体上关注我。 您可以通过我的推特linkedIn帐户与我联系。

谢谢阅读。 很快就会有更多教程变得更好。

再见!!!

From: https://hackernoon.com/kvo-kvc-in-swift-12f77300c387

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值