KVC\KVO介绍_请简述什么是kvc、kvo_guicl0219的博客-程序员秘密

技术标签: KVO  KVC  IOS  

转载自:http://blog.csdn.net/zhaozy55555/article/details/8598374

仅供自我学习,感谢楼主。



一.KVC和KVO的概念

1> KVC:NSKeyValueCoding的简称,是一种可以直接通过字符串的名字(key)来访问类属性的机制,而不是通过调用的Setter、Getter方法访问。

2> KVO:NSKeyValueObserving的简称,当指定的对象的属性被修改了,允许对象接收到通知的机制。

二.KVC介绍

1、概述

KVCKeyValue Coding的简称,它是一种可以直接通过字符串的名字(key)来访问类属性的机制。而不是通过调用SetterGetter方法访问。

当使用KVOCore DataCocoaBindingsAppleScript(Mac支持)时,KVC是关键技术。

2、如何使用KVC

关键方法定义在:NSKeyValueCodingprotocol

KVC支持类对象和内建基本数据类型。

  获取值

valueForKey:,传入NSString属性的名字。

valueForKeyPath:,传入NSString属性的路径,xx.xx形式。

valueForUndefinedKey它的默认实现是抛出异常,可以重写这个函数做错误处理。

  修改值

setValue:forKey:

setValue:forKeyPath:

setValue:forUndefinedKey:

setNilValueForKey: 当对非类对象属性设置nil时,调用,默认抛出异常。

       一对多关系成员的情况

mutableArrayValueForKey:有序一对多关系成员  NSArray

mutableSetValueForKey:无序一对多关系成员  NSSet

3、KVC的实现细节

  搜索SetterGetter方法

 这一部分比较重要,能让你了解到KVC调用之后,到底是怎样获取和设置类成员值的。

   搜索简单的成员

     如:基本类型成员,单个对象类型成员:NSIntegerNSString*成员。

   a. setValue:forKey的搜索方式:

     首先搜索set<Key>:方法

      如果成员用@property@synthsize处理,因为@synthsize告诉编译器自动生成set<Key>:格式的setter方法,所以这种情况下会直接搜索到。

      注意:这里的<Key>是指成员名,而且首字母大写。下同。

     上面的setter方法没有找到,如果类方法accessInstanceVariablesDirectly返回YES(注:这是NSKeyValueCodingCatogery中实现的类方法,默认实现为返回YES)

     那么按_<key>_is<Key><key>is<key>的顺序搜索成员名。

     如果找到设置成员的值,如果没有调用setValue:forUndefinedKey:

   b. valueForKey:的搜索方式:

1. 首先按get<Key><key>is<Key>的顺序查找getter方法,找到直接调用。如果是boolint等内建值类型,会做NSNumber的转换。

2. 上面的getter没有找到,查找countOf<Key>objectIn<Key>AtIndex:<Key>AtIndexes格式的方法。

如果countOf<Key>和另外两个方法中的一个找到,那么就会返回一个可以响应NSArray所有方法的代理集合(collection proxy object)。发送给这个代理集合(collection proxy object)NSArray消息方法,就会以countOf<Key>objectIn<Key>AtIndex:<Key>AtIndexes这几个方法组合的形式调用。还有一个可选的get<Key>:range:方法。

3. 还没查到,那么查找countOf<Key>enumeratorOf<Key>memberOf<Key>:格式的方法。

如果这三个方法都找到,那么就返回一个可以响应NSSet所有方法的代理集合(collection proxy object)。发送给这个代理集合(collection proxy object)NSSet消息方法,就会以countOf<Key>enumeratorOf<Key>memberOf<Key>:组合的形式调用。

4. 还是没查到,那么如果类方法accessInstanceVariablesDirectly返回YES,那么按_<key>_is<Key><key>is<key>的顺序直接搜索成员名。

5. 再没查到,调用valueForUndefinedKey:

查找有序集合成员,比如NSMutableArray

mutableArrayValueForKey:搜索方式如下:

1. 搜索insertObject:in<Key>AtIndex:removeObjectFrom<Key>AtIndex:或者insert<Key>:atIndexesremove<Key>AtIndexes:格式的方法。

如果至少一个insert方法和至少一个remove方法找到,那么同样返回一个可以响应NSMutableArray所有方法的代理集合。那么发送给这个代理集合的NSMutableArray消息方法,以insertObject:in<Key>AtIndex:removeObjectFrom<Key>AtIndex:insert<Key>:atIndexesremove<Key>AtIndexes:组合的形式调用。还有两个可选实现的接口:replaceObjectIn<Key>AtIndex:withObject:replace<Key>AtIndexes:with<Key>:

2. 否则,搜索set<Key>:格式的方法,如果找到,那么发送给代理集合的NSMutableArray最终都会调用set<Key>:方法。

也就是说,mutableArrayValueForKey取出的代理集合修改后,用set<Key>:重新赋值回去。这样做效率会差很多,所以推荐实现上面的方法。

3. 否则,那么如果类方法accessInstanceVariablesDirectly返回YES,那么按_<key><key>的顺序直接搜索成员名。如果找到,那么发送的NSMutableArray消息方法直接转交给这个成员处理。

4. 再找不到,调用setValue:forUndefinedKey:

搜索无序集合成员,如:NSSet

mutableSetValueForKey:搜索方式如下:

1. 搜索add<Key>Object:remove<Key>Object:或者add<Key>:remove<Key>:格式的方法,如果至少一个insert方法和至少一个remove方法找到,那么返回一个可以响应NSMutableSet所有方法的代理集合。那么发送给这个代理集合的NSMutableSet消息方法,以add<Key>Object:remove<Key>Object:add<Key>:remove<Key>:组合的形式调用。还有两个可选实现的接口:intersect<Key>set<Key>:

2. 如果recieverManagedObejct,那么就不会继续搜索了。

3. 否则,搜索set<Key>:格式的方法,如果找到,那么发送给代理集合的NSMutableSet最终都会调用set<Key>:方法。也就是说,mutableSetValueForKey取出的代理集合修改后,用set<Key>:重新赋值回去。这样做效率会差很多,所以推荐实现上面的方法。

4. 否则,那么如果类方法accessInstanceVariablesDirectly返回YES,那么按_<key><key>的顺序直接搜索成员名。如果找到,那么发送的NSMutableSet消息方法直接转交给这个成员处理。

5. 再找不到,调用setValue:forUndefinedKey:

KVC还提供了下面的功能

值的正确性核查

KVC提供属性值确认的API,它可以用来检查set的值是否正确、为不正确的值做一个替换值或者拒绝设置新值并返回错误原因。

实现核查方法

为如下格式:validate<Key>:error:

如:

-(BOOL)validateName:(id *)ioValue error:(NSError **)outError  

 

    // The name must not be nil, and must be at least two characters long.   

    if ((*ioValue == nil) || ([(NSString *)*ioValue length] 2])  

        if (outError != NULL)  

            NSString *errorString NSLocalizedStringFromTable(  

                    @"A Person's name must be at least two characters long", @"Person",  

                    @"validation: too short name error");  

            NSDictionary *userInfoDict  

                [NSDictionary dictionaryWithObject:errorString  

                                            forKey:NSLocalizedDescriptionKey];  

            *outError [[[NSError alloc] initWithDomain:PERSON_ERROR_DOMAIN  

                                                    code:PERSON_INVALID_NAME_CODE  

                                                userInfo:userInfoDict] autorelease];  

         

        return NO;  

     

    return YES;  

 

调用核查方法: 

validateValue:forKey:error:,默认实现会搜索 validate<Key>:error:格式的核查方法,找到则调用,未找到默认返回YES

注意其中的内存管理问题。

集合操作

集合操作通过对valueForKeyPath:传递参数来使用,一定要用在集合(如:array)上,否则产生运行时刻错误。其格式如下:

Left keypath部分:需要操作对象路径。

Collectionoperator部分:通过@符号确定使用的集合操作。

Rightkey path部分:需要进行集合操作的属性。

1、数据操作

@avg:平均值

@count:总数

@max:最大

@min:最小

@sum:总数

确保操作的属性为数字类型,否则运行时刻错误。

2、对象操作

针对数组的情况

@distinctUnionOfObjects:返回指定属性去重后的值的数组

@unionOfObjects:返回指定属性的值的数组,不去重

属性的值不能为空,否则产生异常。

3、数组操作

针对数组的数组情况

@distinctUnionOfArrays:返回指定属性去重后的值的数组

@unionOfArrays:返回指定属性的值的数组,不去重

@distinctUnionOfSets:同上,只是返回值为NSSet

三.KVC实现分析

KVC运用了一个isa-swizzling技术。isa-swizzling就是类型混合指针机制。KVC主要通过isa- swizzling,来实现其内部查找定位的。isa指针,如其名称所指,(就是is a kind of的意思),指向维护分发表的对象的类。该分发表实际上包含了指向实现类中的方法的指针,和其它数据。

    比如说如下的一行KVC的代码:

[site setValue:@"sitename" forKey:@"name"];


就会被编译器处理成:

SEL sel = sel_get_uid ("setValue:forKey:");
IMP method = objc_msg_lookup (site->isa,sel);
method(site, sel, @"sitename", @"name");


    首先介绍两个基本概念:

    (1)SEL数据类型:它是编译器运行Objective-C里的方法的环境参数。

    (2)IMP数据类型:他其实就是一个 编译器内部实现时候的函数指针。当Objective-C编译器去处理实现一个方法的时候,就会指向一个IMP对象,这个对象是C语言表述的类型(事实 上,在Objective-C的编译器处理的时候,基本上都是C语言的)。

    关于如何找到实现函数的指针,可参考文章:《Objective-C如何避免动态绑定,而获得方法地址》:http://www.cocoadev.cn/Objective-C/Get-method-address.asp

    这下KVC内部的实现就很清楚的清楚了:一个对象在调用setValue的时候,(1)首先根据方法名找到运行方法的时候所需要的环境参数。(2)他会从自己isa指针结合环境参数,找到具体的方法实现的接口。(3)再直接查找得来的具体的方法实现。


四.KVO介绍


Kvo是Cocoa的一个重要机制,他提供了观察某一属性变化的方法,极大的简化了代码。这种观察-被观察模型适用于这样的情况,比方说根据A(数 据类)的某个属性值变化,B(view类)中的某个属性做出相应变化。对于推崇MVC的cocoa而言,kvo应用的地方非常广泛。(这样的机制听起来类 似Notification,但是notification是需要一个发送notification的对象,一般是 notificationCenter,来通知观察者。而kvo是直接通知到观察对象。)

适用kvo时,通常遵循如下流程:

1 注册:

- ( void )addObserver:(NSObject  * )anObserver forKeyPath:(NSString  * )keyPath options:(NSKeyValueObservingOptions)options context:( void * )context

keyPath就是要观察的属性值,options给你观察键值变化的选择,而context方便传输你需要的数据(注意这是一个void型)

2 实现变化方法:

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

change里存储了一些变化的数据,比如变化前的数据,变化后的数据;如果注册时context不为空,这里context就能接收到。

是不是很简单?kvo的逻辑非常清晰,实现步骤简单。

说了这么多,大家都要跃跃欲试了吧。可是,在此之前,我们还需要了解KVC机制。其实,知道了kvo的逻辑只是帮助你理解而已,要真正掌握的,不在 于kvo的实现步骤是什么,而在于KVC,因为只有符合KVC标准的对象才能使用kvo(强烈推荐要使用kvo的人先理解KVC)。

KVC是一种间接访问对象属性(用字符串表征)的机制,而不是直接调用对象的accessor方法或是直接访问成员对象。

key就是确定对象某个值的字符串,它通常和accessor方法或是变量同名,并且必须以小写字母开头。Key path就是以“.”分隔的key,因为属性值也能包含属性。比如我们可以person这样的key,也可以有key.gender这样的key path。

获取属性值时可以通过valueForKey:的方法,设置属性值用setValue:forKey:。与此同时,KVC还对未定义的属性值定义了 valueForUndefinedKey:,你可以重载以获取你要的实现(补充下,KVC定义载NSKeyValueCoding的非正式协议里)。

在O-C 2.0引入了property,我们也可以通过.运算符来访问属性。下面直接看个例子:

@property NSInteger number;

instance.number 
= 3 ;
[instance setValue:[NSNumber numberWithInteger:
3 ] forKey: @" number " ];

注意KVC中的value都必须是对象。

以上介绍了通过KVC来获取/设置属性,接下来要说明下实现KVC的访问器方法(accessor method)。Apple给出的惯例通常是:

-key:,以及setKey:(使用的name convention和setter/getter命名一致)。对于未定义的属性可以用setNilValueForKey:。

至此,KVC的基本概念你应该已经掌握了。之所以是基本,因为只涉及到了单值情况,kvc还可以运用到对多关系,这里就不说了,留给各位自我学习的空间

接下来,我们要以集合为例,来对掌握的KVC进行一下实践。

之所以选择array,因为在ios中,array往往做为tableview的数据源,有这样的一种情况:

 假设我们已经有N条数据,在进行了某个操作后,有在原先的数据后多了2条记录;或者对N中的某些数据进行更新替换。不使用KVC我们可以使用 reloadData方法或reloadRowsAtIndexPaths。前一种的弊端在于如果N很大消耗就很大。试想你只添加了几条数据却要重载之前 N数据。后一种方法的不足在于代码会很冗余,你要一次计算各个indexPath再去reload,而且还要提前想好究竟在哪些情况下会引起数据更新,

倘若使用了KVC/kvo,这样的麻烦就迎刃而解了,你将不用关心追加或是更新多少条数据。

下面将以添加数据为例,说明需要实现的方法:

实现insertObject:inKeyAtIndex:或者insertKey:atIndexes。同时在kvo中我们可以通过change这个dictionary得知发生了哪种变化,从而进行相应的处理。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/guicl0219/article/details/8933867

智能推荐

phplib简明教程_f5key的博客-程序员秘密

phplib简明教程 phplib基本安装和使用 要想使用phplib强大的功能,有几种途径可以达到目的。 1.采用phplib建议的安装方式,说安装,其实就是把phplib的类预先让解析器加载到内存,这样使用的时候就不需要做任何设置,这样的好处显而易见。不过缺点也是很

【Http协议】Https简介_tpasta的博客-程序员秘密

HTTP 的缺点到现在为止,我们已了解到 HTTP 具有相当优秀和方便的一面,然而 HTTP 并非只有好的一面,事物皆具两面性,它也是有不足之处的。HTTP 主要有这些不足,例举如下。1、通信使用明文( 不加密) , 内容可能会被窃听2、不验证通信方的身份, 因此有可能遭遇伪装2、不验证通信方的身份, 因此有可能遭遇伪装3、无法证明报文的完整性, 所以有可能已遭篡改这些问题不仅在...

OpenCV(1): 环境搭建(VS2017 + OpenCV 4.2.0)_在vs2017下opencv4.2.0安装教程_TechArtisan6的博客-程序员秘密

文章目录1 简要步骤2 详细步骤2.1 安装VS20172.2 下载OpenCV 4.2.02.3 配置环境变量2.4 引入头文件、库文件、链接库3 测试代码参考资料注:转载请标明原文出处链接:https://xiongyiming.blog.csdn.net/article/details/1041458121 简要步骤(1) 安装VS2017;(2) 下载OpenCV 4.2.0 ...

TensorFlow2.0学习笔记-----维度变换_北航_Curry的博客-程序员秘密

1. 对图片信息可以有下面四种理解方式:[b, 28*28]: b代表batch-图片的数量,28*28是把图片所有像素变成了一个轴axis来理解[b, 2,14*28]:表示把图片分成了上下两部分,每一部分当成一个单独的轴axis2.reshape函数-1代表不需要自己去计算这个位置是多少尺寸,系统根据reshape里其他参数和图片原来的尺寸来自动设置reshape操作经常在全连接层前遇到,把图片变成一个向量来储存...

对模型评估中的准确率、精准率和召回率描述与理解_请问模型训练后的准确度,精确度和召回率分别是怎么计算的?_Hogan180的博客-程序员秘密

在机器学习、深度学习中,我们在做分类任务时,经常需要对模型结果进行评估。其中用于评估的指标就有准确率、精准率和召回率,这些指标都是通过预测概率来获得的。以下就来介绍这些指标代表什么。我们先来看看下面这张图:其中,如上图混淆矩阵所示。TP:样本为正,预测结果为正; FP:样本为负,预测结果为正; TN:样本为负,预测结果为负; FN:样本为正,预测结果为负。准确率、精准率和召回率...

随便推点

cocos2dx, 编译android 的NDK_MODULE_PATH的设置_cocos2dx ndk module path_大龄美飘的博客-程序员秘密

cocos2d-x 3.0之后,编译android项目,NDK_MODULE_PATH根本不用设置了。注意项目下build_native.py里面写的 if platform == 'win32': ndk_module_path = 'NDK_MODULE_PATH=%s;%s/external;%s/cocos' % (cocos_root, cocos_ro

卷积神经网络详解_djb847279的博客-程序员秘密

卷积神经网络转载请注明:http://blog.csdn.net/stdcoutzyx/article/details/41596663自今年七月份以来,一直在实验室负责卷积神经网络(Convolutional Neural Network,CNN),期间配置和使用过theano和cuda-convnet、cuda-convnet2。为了增进CNN的理解和使用,特写此博文,以其与...

emoji 问题总结_es ik增加emoji表情字典__starking的博客-程序员秘密

一、不同的emoji插入到mysql表中,出现唯一键冲突业务场景:手机号注册用户,默认昵称为 宝宝+emoji+手机尾号,用户表中昵称字段要求唯一不可重复,存在唯一键。 (已使用utf8mb4字符集)问题:1、插入时 产生唯一键冲突 原因:字符集使用了utf8mb4,但它的默认排序集是utf8mb4_general_ci 。utf8mb4_general_ci 不区分特殊字符,不区分大小写。概念:字符:文字与符号的总称,包括文字、图形符号、数学符号等。英...

vuex中commit和dispatch的区别_vuex dispatch_Excellent !的博客-程序员秘密

commit 和dispatch的区别在于commit是提交mutatious的同步操作,dispatch是分发actions的异步操作dispatch:含有异步操作,例如向后台提交数据,写法: this.$store.dispatch(‘action方法名’,值)commit:同步操作,写法:this.$store.commit(‘mutations方法名’,值)如图...

重磅发布- Java秒杀系统的设计与实战视频教程(SpringBoot版)_修罗debug的博客-程序员秘密

概要介绍:历经一个多月的时间,debug亲自录制的“Java商城秒杀系统的设计与实战视频教程(SpringBoot版)”终于完成了!在本课程中,debug真正的将之前所讲解的相关技术融入到了本课程中,即本课程所介绍的“秒杀系统”是一个真正意义上的项目,主要介绍了商城平台举办商品秒杀活动期间所涉及的相关业务。值得介绍的是,本课程在技术栈层面涵盖了前端和后端的大部分常用技术,包括Spring Bo...

如何使输入框只能输入数字,并且中文输入(回车)无效_凳子丶的博客-程序员秘密

记录一下。。。&lt;el-input v-model="searchtext" type="text" :oninput="myInput()"&gt;&lt;/el-input&gt;//用的是vue+element框架,然后在methods方法下 myInput(){ if(/[^\d]/.test(this.searchtext) == true){ ...

推荐文章

热门文章

相关标签