• iOS kvc


    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存取二进制库中的私有成员也比較有用。
    事实上,平均开发者,并不需要用太多。


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

  • 相关阅读:
    Android 逐帧动画isRunning 一直返回true的问题
    android动画学习
    android信号强度
    在代码中创建Drawable资源
    通过ComponentName获取相应的Widget
    EditText和输入法相关的问题
    Android 沉浸式状态栏
    安防设置功能出现的错误
    自定义seekbar
    android 新控件 AppBarLayout 使用
  • 原文地址:https://www.cnblogs.com/yxwkf/p/4756444.html
Copyright © 2020-2023  润新知