细说KVO KVC NSNotificationCenter那些事

2024-02-08 07:38

本文主要是介绍细说KVO KVC NSNotificationCenter那些事,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在iOS开发过程中,我们经常会听到或者用到KVO,KVC,NSNotificationCenter等,但是很多时候,我们可能没有那么了解,下面让我们来详细了解下他们的概念、用法以及他们之间的关系吧~

本篇博客共分以下几个模块来介绍:

  • 什么是KVC?
  • 什么是KVO?
  • KVC与KVO的关系
  • KVC Collection Operators
  • 什么是NSNotificationCenter?
  • NSNotificationCenter与KVO的比较
  • NSNotificationCenter与Delegate的比较

下面就进入正题吧~

什么是KVC?

1. KVC(键值编码)的概念:

苹果的官方文档描述: KVC(Key-Value-Coding)是一种通过字符串描述而不是通过调用访问方法或者直接使用实例变量的非直接的访问对象属性的机制。

KVC 是观察者模式在Objective-C中的实现之一,它以分类(非正式协议)的形式被定义在NSObject中,从协议的角度看,是定义了一套让开发者遵守的规范和使用方法。

2. KVC的用途

在Cocoa的MVC框架中,KVC是ViewControler和Model沟通的桥梁。

KVC的基本方法都定义在了NSKeyValueCoding的非正式协议中,如下图所示:(NSObject默认实现了该协议,也就是说OC中几乎所有的对象都支持KVC操作)。

@interface NSObject(NSKeyValueCoding)
- (nullable id)valueForKey:(NSString *)key;
- (void)setValue:(nullable id)value forKey:(NSString *)key;
- (nullable id)valueForKeyPath:(NSString *)keyPath;
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;

KVC可以用来访问对象的属性、一对一的关系对象、一对多的关系对象

  1. 访问对象属性:也可以是对象的成员变量,成员变量是私有的也可以访问,属性可以是对象,也可以是数值类型和结构体,非对象类型的参数和返回值会自动封装成NSValue活着NSNumber类型。
    valueForKey:会返回跟接收者相关的key的值,如果对于指定的key没有访问器或者实例变量,则给自己发送一个valueForUndefineKey:消息,这个方法的默认实现是抛出一个NSUndefinedKeyException
  2. 通过关系访问对象:假设对象person有属性address,属性address有属性city,我们可以通过person来访问city:

    [person valueForKeyPath:@"address.city"];

    valueForKeyPath:返回跟接收者相关的键路径的值,对于子系列中任何不遵循KVC的对象,都会收到一个valueForUndefineKey:消息。

  3. 访问集合对象: 可以是可变集合和不可变集合,可变集合与KVO结合,可以实现批量更新(需传入多个对象)的功能。

    dictionaryWithValuesForKeys:会检索数组中所有跟接收者相关的key的值,返回的NSDictionary中包含了数组中所有key的值。

KVC可以设置属性的值


setValue:forKey用来将接收者中相关key的值设置成指定的值。在这个方法的实现中,会将NSValue的值转换成普通的数值然后赋值给属性。

KVV(Key-Value-Validate)键值验证

- (BOOL)validateValue:(inout id *)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;

什么是KVO?

  1. 观察者模式的基本思想:

    观察者模式主要是,通过一个对象来管理所有依赖于它的观察者对象,并在它自身的状态改变时主动通知观察者对象。 目标对象通知观察者通常是通过调用各观察对象所提供的接口方法来实现的.观察者模式比较完美的将目标对象与观察者对象解耦.

  2. KVO 的应用场景:

    当一个对象的特定属性改变的时候,需要被通知一个或者多个对象的时候。

  3. KVO 的使用流程:

    • 注册与解除注册

      keyPath不可以为nil
      - (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
      - (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;

    • 设置Value

      [target setAge:30]; //setter
      [target setValue:[NSNumber numberWithInt:30] forKey:@"age"]; //setValue:forKey

    • 处理变更通知

      观察者需要实现名为 NSKeyValueObserving 的 category 方法来处理收到的变更通知

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

    当一个观察者需要观察多个对象的同一个keyPath时,可以通过设置context来区分不同的通知。

  4. 属性依赖

    简单点说,就是通过观察一个key的值,来观察多个属性的变化,需要对象实现以下几个方法。

    + (NSSet *)keyPathsForValuesAffectingAddress
    {
    NSSet *set = [NSSet setWithObjects:@"firstName", @"lastName", nil];
    return set; //这方法使得 Key 之间能够建立依赖关系
    }
    //设置属性之间的依赖关系
    - (NSString *)address
    {
    return [NSString stringWithFormat:@"%@%@",self.province,self.street];
    }

KVC与KVO的关系

KVO是基于KVC实现的,只有我们调用KVC去访问key值的时候KVO才会起作用。

KVC Collection Operators(KVC集合操作符)

  1. KVC 集合操作符的概念:

    KVC集合操作符允许在valueForKeyPath:方法中使用key path符号“@”,在一个集合中执行方法。结果可以被返回或者链接。

  2. KVC 集合操作符的类型(根据返回值的类型来分):

    • 简单的集合操作符:返回值是NSString、NSNumber或者NSDate
    • 对象操算符:返回值是一个数组
    • 数组和集合操作符:返回值是一个数组或者集合
  3. 简单的集合操作符

    • @count:返回一个值为集合中对象总数的NSNumber对象
    • @sum: 首先把集合中的每个对象都转换为double类型,然后计算其总,最后返回一个值为这个总和的NSNumber对象
    • @avg: 首先把集合中的每个对象都转换为double类型,然后计算其平均值,最后返回一个值为这个总和的NSNumber对象
    • @max: 使用compare:方法来确定最大值。所以为了让其正常工作,集合中所有的对象都必须支持和另一个对象的比较
    • @min: 和@max一样,但是返回的是集合中的最小值。
    • 使用示例:

      products是数组,数组中存放了很多对象,每个对象都有一个price的属性。
      [products valueForKeyPath:@"@sum.price"];
      可以用self作为操作符后面的keyPath来获取一个由NSNumber组成的数组或者集合的总值。
      [@[@1,@2] valueForKey:@"@max.self"];

  4. 对象操作符

    • @distinctUnionOfObjects:获取数组中每个对象的属性的值,放到一个数组中并返回,会对数组去重。
    • @unionOfObjects: 同@distinctUnionOfObjects,但是不去重。
    • 使用示例:

      Person *lilei = [[Person alloc] init];
      lilei.name = @"LiLei";
      Person *hanMeiMei = [[Person alloc] init];
      hanMeiMei.name = @"hanMeiMei";
      NSArray *array = @[lilei, hanMeiMei];
      NSLog(@"array is %@",[array valueForKeyPath:@"@distinctUnionOfObjects.name"]);
      输出结果为:
      2015-11-10 16:08:07.977 TestPro[49746:521302] array is (
      LiLei,
      hanMeiMei
      )

  5. 数组和集合操作符

    • @distinctUnionOfArrays: 获取数组中每个数组中的每个对象的属性的值,放到一个数组中并返回,会对数组去重复。
    • @unionOfArrays:同@distinctUnionOfArrays,但是不去重。
    • @distinctUnionOfSets: 获取集合中每个集合中的每个对象的属性的值,放到一个集合中并返回。
    • 使用示例:

      Person *lilei = [[Person alloc] init];
      lilei.name = @"LiLei";
      Person *hanMeiMei = [[Person alloc] init];
      hanMeiMei.name = @"hanMeiMei";
      NSArray *array = @[lilei, hanMeiMei];
      NSLog(@"array is %@",[ @[array,array] valueForKeyPath:@"@unionOfArrays.name"]);
      输出结果为:
      2015-11-10 16:51:26.137 TestPro[50404:556930] array is (
      LiLei,
      hanMeiMei,
      LiLei,
      hanMeiMei
      )

什么是NSNotificationCenter?

  1. 通知中心的概念:

    通知中心是 Foundation 框架的一个子系统,也是一个观察者模式,它向应用程序中注册为某个事件观察者的所有对象广播消息(即通知),可以在不同类之间通信的时候使用。

    • 注意当接受到消息后,不想再收到消息了,要把observer删除remove。一般在类销毁的时候remove。
  2. 组成通知中心的两个类:

    • NSNotificationCenter:获取NSNotificationCenter的方法只有一种,即[NSNotificationCenter defaultCenter],并且NSNotificationCenter是一个单例模式,一旦创建,这个通知中心的对象会一直存在于一个应用的生命周期。
    • NSNotification: 这是消息携带的载体,通过它,可以把消息内容传递给观察者。
  3. 通知中心的使用流程:

    • 注册通知:NSNotificationCenter 注册观察者对某个事件(以字符串命名)感兴趣,及该事件触发时该执行的 Selector 或 Block.

      [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notificationMethod:) name:@"notificationName" object:nil];
      参数注释:
      第一个参数(self):负责监听的对象
      第二个参数(@selector(notificationMethod:)):接收到通知之后,监听对象需要执行的方法。
      第三个参数(@"notificationName"):通知的名称,也是通知的唯一标示,编译器就是通过这个找到通知的。
      第四个参数(nil):最后一个参数是表示会对哪个发送者对象发出的事件作出响应,nil 时表示接受所有发送者的事件。

    • 发送通知:手动发送消息

      [[NSNotificationCenter defaultCenter] postNotificationName:@"notificationName" object:test userInfo:@{@"key":@"value"}];
      参数注释:
      第一个参数(@"notificationName"):通知的名称,这个名称必须和后面接收通知的名称一致
      第二个参数(test):可以传递的一个参数对象
      第三个参数(@{@"key":@"value"}):可以传递的,与通知相关的信息,

    • 移除通知:在观察者不再接收消息的时候,移除通知

      - (void)dealloc
      {
      //移除指定的通知,不然会造成内存泄露
      [[NSNotificationCenter defaultCenter] removeObserver:self name:@"happyValueNotification" object:nil];
      //移除所有的通知
      [[NSNotificationCenter defaultCenter] removeObserver:self];

NSNotificationCenter与KVO的比较

  1. 相同点:
    • NSNotificationCenter与KVO的实现原理都是观察者模式。用于监听操作
  2. 不同点:
    • KVO只用来监听属性值的变化,这个发送监听的操作是系统控制的,我们控制不了,我们只能控制监听操作,类似于Android中系统发送的广播。
    • 我们可以控制通知的发送监听,可以在任何地方任何时机发送一个通知。类似于Android中开发者自己发送的广播。
    • 通知的使用场景比KVO更为广泛些
    • 通知需要一个发送notification的对象,一般是notificationCenter,来通知观察者。KVO是直接通知到观察对象,并且逻辑非常清晰,实现步骤简单。
    • 总之,KVO操作没有通知灵活。但是KVO也有自己的优点,比如可以记录新旧值,通知就比较麻烦,所以在使用的时候还是具体问题具体分析,一般监听属性值的变化,最好还是用KVO。

NSNotificationCenter与Delegate的比较

通知比Deleagte可以实现更大跨度的通信机制,可以为两个无引用关系的两个对象进行通信,即事件发出者和响应者可以没有任何耦合关系。

这篇关于细说KVO KVC NSNotificationCenter那些事的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/690342

相关文章

iOS--KVO的实现原理与具体应用

iOS--KVO的实现原理与具体应用 本文分为2个部分:概念与应用。 概念部分旨在剖析KVO这一设计模式的实现原理,应用部分通过创建的项目,以说明KVO技术在iOS开发中所带来的作用; 如果是作为是刚接触KVO的初学者,可以在了解基本原理后粗略看几遍底层实现原理,再认真阅读第二部分的应用内容“学会”怎么去使用KVO,往后再慢慢深入了解KVO这一“黑魔法”技术的实现原理。 【本次开发环境:

NSNotification、delegate和KVO的…

1.效率肯定是delegate比nsnotification高。 2. delegate方法比notification更加直接,最典型的特征是,delegate方法往往需要关注返回值, 也就是delegate方法的结果。比如-windowShouldClose:,需要关心返回的是yes还是no。所以delegate方法往往包含 should这个很传神的词。也就是好比你做我的delegat

NSNotificationCenter用法总结

NSNotificationCenter未必一定要建在消息接收者的类中。可以放在别的类中,先实例化一下,然后把observer赋值为刚对象。 这里的observer相当于接受者(receiver),object相当于发送者(poster)。理解了这点就可以较灵活地使用通知了。 iPhone软件开发的时候会遇到这种情况:打开APP后会在后台运行某个方法,例如下载文件,下载完成后可能需要调用某

轻量级KVO ——》 KVO 管理 observeValueForKeyPath

【转】http://joeyio.com/2013/10/21/lightweight_kvo/ 在这篇文章中,我会实现一个自己用的简单KVO类,我认为KVO非常棒,然而对于我大部分的使用场景来说,有这两个问题: 1. 我不喜欢在observeValueForKeyPath:ofObject:change:context:方法里通过keyPath值来做调度,当Observ

KVO详解及底层实现

什么是KVO?? KVO就是NSKeyValueObserving,请看官方文档的解释: 大概翻译如下: 一种非正式协议,通知其他对象的指定属性发生了改变。 简单理解就是,可以监听一个对象的某个属性是否发生改变。 那么问题来了,什么是非正式协议??有正式协议吗?? 麻蛋,本来想找官方文档的,找了半天没找到。从Stackoverflow找到了答案,貌似原来官方文档的链接失效了

GitHub爆火!美团面试官细说:测试工程师面试要注意什么?已帮我拿下6个offer!

找工作,找更好的工作,永远是职场人士特别是IT/互联网这个人才流动性巨大行业的永恒话题。而提到找工作,又离不开对于面试的探讨。 网上虽然有诸多面试相关的文章攻略,不过站在面试官角度谈面试的却很少。 本文就站在面试官的角度,谈一谈一个面试是怎么组织的,有哪些技巧和思路,希望帮到开始接触招聘任务的测试管理人员,同时也从另一个角度帮助求职人员应对面试。 1、常见招聘流程 一个测试团队的组建过程是

(译)KVO的内部实现

转自:http://www.cocoachina.com/applenews/devnews/2014/0107/7667.html 09年的一篇文章,比较深入地阐述了KVO的内部实现。 KVO是实现Cocoa Bindings的基础,它提供了一种方法,当某个属性改变时,相应的objects会被通知到。在其他语言中,这种观察者模式通常需要单独实现,而在Objective-C中,通

iOS OC底层面试题(KVO (Key-value observing))

KVO (Key-value observing) KVO是观察者模式的另一实现。 使用了isa混写(isa-swizzling)来实现KVO 使用setter方法改变值KVO会生效,使用setValue:forKey即KVC改变值KVO也会生效,因为KVC会去调用setter方法 - (void)setValue:(id)value{[self willChangeValueForKey

iOS OC底层面试题(KVC(Key-value coding)

KVC(Key-value coding) -(id)valueForKey:(NSString *)key;-(void)setValue:(id)value forKey:(NSString *)key; KVC就是指iOS的开发中,可以允许开发者通过Key名直接访问对象的属性,或者给对象的属性赋值。而不需要调用明确的存取方法。这样就可以在运行时动态地访问和修改对象的属性。而不是在编译时

细说MCU定时器模块的输入捕捉功能的实现方法

目录 一、工程背景  二、建立工程 1、配置GPIO  2、选择时钟源和Debug 3、 配置定时器TIM1 4、 配置定时器TIM13 5、配置串口 6、配置中断 7、配置系统时钟 三、代码修改  1、使能TIM1输入捕捉功能和TIM3的PWM输出功能 2、自定义变量 3、重定义回调函数 4、输出到串口  四、查看结果 五、测量脉冲宽度 一、工程背景