iOS kvc

来源:互联网 发布:python url编码 编辑:程序博客网 时间:2024/06/06 02:40

kvc在我的脑海里就是一个用来来修改实例变量属性的值。

今天又遇到kvc来第二次学习它,网上看了很多博客,好像都不太符合我的口味,下面来摘录一些,自己总结一下;

http://www.cnblogs.com/stoic/archive/2012/07/20/2601315.html

这个博主写的是一些应用实例,我比较喜欢,他说明了,如何去代码操作;

http://blog.csdn.net/omegayy/article/details/7381301

这个博主是主要的原理;

下面是自己感觉有用的东西从两位博客中摘录的;

概述

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

当使用KVO、Core Data、CocoaBindings、AppleScript(Mac支持)时,KVC是关键技术


如何使用KVC

关键方法定义在:NSKeyValueCodingprotocol

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

       获取值

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

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

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

        修改值

setValue:forKey:

setValue:forKeyPath:

setValue:forUndefinedKey:

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

       一对多关系成员的情况

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

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


下面是一些操作实例:

1、使用KVC

#import <Foundation/Foundation.h>@interface Student : NSObject{    NSString *name;}@end#import "Student.h"int main(int argc, const char * argv[]){    @autoreleasepool {        Student *student = [[[Student alloc]init ]autorelease];        [student setValue:@"张三" forKey:@"name"];        NSString *name = [student valueForKey:@"name"];        NSLog(@"学生姓名:%@",name);    }    return 0;}
2、键路径访问属性
#import <Foundation/Foundation.h>@interface Course : NSObject{    NSString *CourseName;}@end#import "Course.h"@implementation Course@end#import <Foundation/Foundation.h>@class Course;@interface Student : NSObject{    NSString *name;    Course *course;}@end#import "Student.h"#import "Course.h"int main(int argc, const char * argv[]){    @autoreleasepool {        Student *student = [[[Student alloc]init ]autorelease];        [student setValue:@"张三" forKey:@"name"];        NSString *name = [student valueForKey:@"name"];        NSLog(@"学生姓名:%@",name);                Course *course = [[[Course alloc]init] autorelease];        [course setValue:@"语文课" forKey:@"CourseName"];        [student setValue:course forKey:@"course"];        NSString *courseName = [student valueForKeyPath:@"course.CourseName"];        NSLog(@"课程名称:%@", courseName);                //也可以这样存值        [student setValue:@"数学课" forKeyPath:@"course.CourseName"];        courseName = [student valueForKeyPath:@"course.CourseName"];        NSLog(@"课程名称:%@", courseName);            }    return 0;}
3、自动封装基本数据类型
#import <Foundation/Foundation.h>@class Course;@interface Student : NSObject{    NSString *name;    Course *course;    NSInteger point;}@end#import "Student.h"#import "Course.h"int main(int argc, const char * argv[]){    @autoreleasepool {        Student *student = [[[Student alloc]init ]autorelease];        [student setValue:@"张三" forKey:@"name"];        NSString *name = [student valueForKey:@"name"];        NSLog(@"学生姓名:%@",name);                Course *course = [[[Course alloc]init] autorelease];        [course setValue:@"语文课" forKey:@"CourseName"];        [student setValue:course forKey:@"course"];        NSString *courseName = [student valueForKeyPath:@"course.CourseName"];        NSLog(@"课程名称:%@", courseName);                //也可以这样存值        [student setValue:@"数学课" forKeyPath:@"course.CourseName"];        courseName = [student valueForKeyPath:@"course.CourseName"];        NSLog(@"课程名称:%@", courseName);                [student setValue:@"88" forKeyPath:@"point"];        NSString *point = [student valueForKey:@"point"];        NSLog(@"分数:%@", point);            }    return 0;}


4、操作集合
#import <Foundation/Foundation.h>@class Course;@interface Student : NSObject{    NSString *name;    Course *course;    NSInteger point;    NSArray *otherStudent;}@end#import "Student.h"#import "Course.h"int main(int argc, const char * argv[]){    @autoreleasepool {        Student *student = [[[Student alloc]init ]autorelease];        [student setValue:@"张三" forKey:@"name"];        NSString *name = [student valueForKey:@"name"];        NSLog(@"学生姓名:%@",name);                [student setValue:@"88" forKey:@"point"];        NSString *point = [student valueForKey:@"point"];        NSLog(@"分数:%@", point);                Student *student1 = [[[Student alloc]init]autorelease];        Student *student2 = [[[Student alloc]init]autorelease];        Student *student3 = [[[Student alloc]init]autorelease];        [student1 setValue:@"65" forKey:@"point"];        [student2 setValue:@"77" forKey:@"point"];        [student3 setValue:@"99" forKey:@"point"];        NSArray *array = [NSArray arrayWithObjects:student1,student2,student3,nil];        [student setValue:array forKey:@"otherStudent"];        NSLog(@"其他学生的成绩%@", [student valueForKeyPath:@"otherStudent.point"]);        NSLog(@"共%@个学生", [student valueForKeyPath:@"otherStudent.@count"]);        NSLog(@"最高成绩:%@", [student valueForKeyPath:@"otherStudent.@max.point"]);        NSLog(@"最低成绩:%@", [student valueForKeyPath:@"otherStudent.@min.point"]);        NSLog(@"平均成绩:%@", [student valueForKeyPath:@"otherStudent.@avg.point"]);    }    return 0;}

KVC的实现细节:

搜索Setter、Getter方法

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

 搜索简单的成员

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

a. setValue:forKey的搜索方式:

1. 首先查找设置改属性的setter方法

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

注意:setting方法是泛指 ,指为属性赋值的方法

2. 上面的setter方法没有找到,如果类方法accessInstanceVariablesDirectly(该方法会返回是否直接访问没有生成成访问器的变量)返回YES(注:这是NSKeyValueCodingCatogery中实现的类方法,默认实现为返回YES)。

那么按_name,_isnamename,isname的顺序搜索成员名。(设变量名字为name

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

 

b. valueForKey:的搜索方式:

设变量名字为name

1. 首先按getnamename、isname的顺序查找getter方法,找到直接调用。如果是bool、int等内建值类型,会做NSNumber的转换。

2. 上面的getter没有找到,查找countOfname、objectInnameAtIndex:、nameAtIndexes格式的方法。

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

3. 还没查到,那么查找countOfname、enumeratorOfname、memberOfname:格式的方法。

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

4. 还是没查到,那么如果类方法accessInstanceVariablesDirectly返回YES,那么按_name,_isnamename,isname的顺序直接搜索成员名。

5. 再没查到,调用valueForUndefinedKey:。

 

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

mutableArrayValueForKey:搜索方式如下:

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

如果至少一个insert方法和至少一个remove方法找到,那么同样返回一个可以响应NSMutableArray所有方法的代理集合。那么发送给这个代理集合的NSMutableArray消息方法,以insertObject:in<Key>AtIndex:、removeObjectFrom<Key>AtIndex:、insert<Key>:atIndexes、remove<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:。

 

 NSMutableArray *mutablearray = [kvo mutableArrayValueForKey:@"array"];//kvo为自定义的类里面放了一个array的数组通过这个方法来获取该数组的代理,并在此修改,kvo中的array值就会相应的变化;    [mutablearray removeObject:@"234"];    [mutablearray addObject:@"345"];

2.3.3         搜索无序集合成员,如: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. 如果reciever是ManagedObejct,那么就不会继续搜索了。

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

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

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

 

KVC还提供了下面的功能

2.4  值的正确性核查

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。

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

 

2.5  集合操作

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

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

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

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

2.5.1         数据操作

@avg:平均值

@count:总数

@max:最大

@min:最小

@sum:总数

使用方法在上面例子中可见

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

2.5.2         对象操作

针对数组的情况

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

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

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

2.5.3         数组操作

针对数组的数组情况

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

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

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

 

示例代码:

 

2.6  效率问题

相比直接访问KVC的效率会稍低一点,所以只有当你非常需要它提供的可扩展性时才使用它。


下面摘录一段留言能让我们了解为什么会用kvc:


. KVO是以KVC为基础的,有些监听不使用KVC操作会监听不到。
2. 如果用到Core Data,需要使用KVC做值的存取。(否则你会发现各种各样的问题的)
主要应用场景就是与KVO和Core Data协作。

这种统一的直接通过字符串存取ObjC中对象的成员属性的接口,可以实现由外部脚本控件程序执行或者获取程序执行信息。
通过KVC存取二进制库中的私有成员也比较实用。
普通开发的确不会并且不需要用太多。
1 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 8个月孕妇咳嗽怎么办 孕妇7个月咳嗽怎么办 孕妇6个月喉咙痒怎么办 怀孕了咳嗽的厉害怎么办 买衣服眼光太差怎么办 我买衣服没主见怎么办 海澜之家皮带长怎么办 针织衫袖口松了怎么办 宝宝裤子腰大了怎么办 两岁了头发稀少怎么办 孩子抵抗力差爱生病怎么办 三岁宝宝总生病怎么办 冬天穿伴娘服冷怎么办 白衬衣粘了黑毛怎么办 黄衣服钻进黑毛怎么办 单位没给上社保怎么办 轮毂中心孔大了怎么办 钻戒大了怎么办小窍门 shift加f4没反应怎么办 多洗联系不上怎么办 弹力靴筒往下掉怎么办 子宫内膜厚月经停不了怎么办 雪纺衣服有静电怎么办 阿胶糕熬的稀了怎么办 感冒吃了阿胶糕怎么办 身上起湿疹很痒怎么办 身上起小疹子痒怎么办 吊带裙带子断了怎么办 裙子洗完缩水了怎么办 衬衫洗后缩水了怎么办 羊毛被洗缩水了怎么办 蕾丝洗后缩水了怎么办 天丝针织衫缩水怎么办? 氨纶面料起球怎么办? 新买的衬衫很硬怎么办 孕晚期睡不好觉怎么办 全棉衣服缩水了怎么办 棉麻的衣服起球怎么办 布艺沙发起球怎么办 加绒的衣服掉毛怎么办 新裤子有刺鼻味怎么办