iOS kvc

iOS kvc

大家好,又见面了,我是全栈君,今天给大家准备了Idea注册码。

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。is name的顺序搜索成员名。(设变量名字为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存取二进制库中的私有成员也比較有用。
事实上,平均开发者,并不需要用太多。

版权声明:本文博客原创文章,博客,未经同意,不得转载。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/117141.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • mybatis中resultMap配置细则

    mybatis中resultMap配置细则resultMap算是mybatis映射器中最复杂的一个节点了,能够配置的属性较多,我们在mybatis映射器配置细则这篇博客中已经简单介绍过resultMap的配置了,当时我们介绍了resultMap中的id和result节点,那么在resultMap中除了这两个之外,还有其他节点,今天我们就来详细说说resultMap中的这些节点。如果小伙伴对mybatis尚不了解,建议先翻看博主前面几篇

    2022年10月27日
  • java BigDecimal类型 比较大小

    java BigDecimal类型 比较大小一般我们进行类型比较有.equals()、==、.compareTo()但是当比较bigdecimal类型时最好使用.compareTo()来比较大小BigDecimala=BigDecimal.ZERO;BigDecimalb=BigDecimal.valueOf(0.000);BigDecimalc=BigDecimal.valueOf(0.0);a.equals(b)的结果会是fal…

  • 同时安装wps和office怎样默认office打开_wps表格合并多个文件

    同时安装wps和office怎样默认office打开_wps表格合并多个文件MicrosoftOffice是微软公司的一款办公软件,学习工作中的必备工具。WPS则是由金山推出的新兴免费国产办公软件,也非常的好用,可以手机、平板、电脑三端同步。WPS的功能相对可能会较多,用户群体学生党偏多。但是MicrosoftOffice毕竟是老牌办公软件,商务人士一般使用较多,很多场合下若是有软件要求,基本都会选择需要使用MicrosoftOffice。而且WPS和Office之间相互转换的话,也会有可能出现问题。那么,很多人的电脑上都会同时安装这两款软件。如果我们电脑同时安装了WPS

    2022年10月14日
  • swagger使用「建议收藏」

    swaggerrestfuldemo网络上swagger的配置,大多都是复制粘贴转发的。本人开始的时候参照了配置过,基本都是以失败告终。一怒之下,造死了搞,搭建了一个swagger描述的rest风格的接口demo工程。使用的版本号为spring4+jdk8+swagger0.8.4搭建过程中遇到不少问题,主要是swagger默认依赖的是spring3.与jdk8配合的时候,有点问题。直接将s

  • pycharm 2021.11.3激活补丁【2021.10最新】[通俗易懂]

    (pycharm 2021.11.3激活补丁)本文适用于JetBrains家族所有ide,包括IntelliJidea,phpstorm,webstorm,pycharm,datagrip等。https://javaforall.cn/100143.htmlIntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,上面是详细链接哦~2…

  • Wscript.shell 详解

    Wscript.shell 详解Wscript.shellWscript.shell是Wshshell的ProIDWshshell是一个对象,存放在windows系统中的wshom.ocx文件中,可供在windows上运行的程序调用。他可对windows的外壳程序进行访问。对象(object)是一些相关变量和方法的软件集,或者说库。当你需要这个对象里面的变量,方法,功能的时候,你就可以使用相关语

    2022年10月27日

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号