Objective - C 经验小谈: KVC(Key Value Coding)的使用

除了:getter setter 方法,点语法, ->访问修饰符之外,我们还可以用 KVC(Key-Value-Coding) 键值编码来操作对象。

 

那么究竟如何使用KVC操作对象的属性呢?下面我会用详细的例子来讲解。

1、KVC的简单的赋值与取值

赋值方法:[obj setValue:@"newValue" forKey:@"propertyName"];

取值方法:NSString *  [obj valueForKey:@"propertyName"];

 

1.1.在下面的案例中,我们设计了一个Teacher类,他继承自NSObject,并且拥有一个名为name的字段(属性)。

#import <Foundation/Foundation.h>

@interface Teacher : NSObject
{
    NSString *name;
}
@end

@implementation Teacher

@end

 

1.2. 因为没有加@property关键字,并且没有加@public关键字,所以点语法和->访问修饰符都无法操作该字段,那么怎么办呢?

      在下文的案例中,我们使用KVC(Key Value Coding),将能为该字段赋值,并取出打印。

#import "Teacher.h"

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        Teacher *teacher = [[Teacher alloc] init];
        [teacher setValue:@"星星老师" forKey:@"name"];
        NSString *name = [teacher valueForKey:@"name"];
        NSLog(@"这个世界上最伟大的老师是:%@",name);
    }
    return 0;
}

 

1.3. 那么如果 “key” 和对象的字段名称不一致会发生什么情况呢? 

#import "Teacher.h"


int main(int argc, const char * argv[])
{
    @autoreleasepool {

        Teacher *teacher = [[Teacher alloc] init];

        [teacher setValue:@"其他老师" forKey:@"otherName"];
        //程序在这里崩溃将会崩溃,并且输出结果,表示该类没有名为otherName 的key
        //print results: *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Teacher 0x100110780> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key otherName.'
    }
    return 0;
}

 

2、KVC键路径访问属性

如果访问这个类里中的属性中的属性呢?

那我们会需要用到键路径:keyPath

键路径取值方法:[obj valueForKeyPath:@"propertyName"]

键路径赋值方法:[obj setValue:id forKeyPath:@"propertyName"]

 

2.1. 新建一个类Student,学生类,有name这个字段

#import <Foundation/Foundation.h>

@interface Student : NSObject
{
    NSString *studentName;
}
@end

@implementation Student

@end

 

2.2.在Teacher中添加Student字段:

#import <Foundation/Foundation.h>
@class Student;

@interface Teacher : NSObject
{
    NSString *name;
    Student *student;
}
@end

 

 2.3.在main方法中,我们实验通过键路径访问对象teacher中的字段 student的studentName

#import "Teacher.h"
#import "Student.h"

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        Teacher *teacher = [[Teacher alloc] init];
        [teacher setValue:@"星星老师" forKey:@"name"];
        NSString *teacherName = [teacher valueForKey:@"name"];
        NSLog(@"世界上最伟大的老师是:%@",teacherName);
        
        Student *student = [[Student alloc] init];
        [student setValue:@"小灰灰" forKey:@"studentName"];
        [teacher setValue:student forKey:@"student"];
        NSString *studentName = [teacher valueForKeyPath:@"student.studentName"];
        NSLog(@"%@最漂亮的学生是:%@", teacherName, studentName);
        
        //当然,我们也可以通过“.(点)语法"来赋值
        [teacher setValue:@"大灰灰" forKeyPath:@"student.studentName"];
        studentName = [teacher valueForKeyPath:@"student.studentName"];
        NSLog(@"%@最可爱的学生是:%@",teacherName, studentName);

        /*
            print result is:
            世界上最伟大的老师是:星星老师
            星星老师最漂亮的学生是:小灰灰
            星星老师最可爱的学生是:大灰灰
        */
        
    }
    return 0;
}

 

3、基本数据类型的自动类型推导

3.1.我们在Teacher 类中添加年龄字段 NSInteger age;

#import <Foundation/Foundation.h>
@class Student;
@interface Teacher : NSObject
{
    NSString *name;
    Student *student;
    NSInteger age;
}
@end

 

3.2. 我们用NSString类型设置值@"18",而age字段是NSInteger类型的,赋值取值都没有问题。

#import "Student.h"
#import "Teacher.h"


int main(int argc, const char * argv[])
{
    @autoreleasepool {
        Teacher *teacher = [[Teacher alloc] init];
        [teacher setValue:@"星星老师" forKey:@"name"];
        [teacher setValue:@"18" forKey:@"age"];
        NSString *teacherName = [teacher valueForKey:@"name"];
        NSString *age = [teacher valueForKey:@"age"];
        NSLog(@"世界上最伟大的老师是:%@,他永远%@岁",teacherName, age);
        // print result is:
        // 世界上最伟大的老师是:星星老师,他永远18岁
    }
    return 0;
}

 

4、操作集合NSArray

 知识点:@max,@min,@avg,@sum的使用

4.1. 在Teacher类中加入数组NSArray *students,这样我们可以添加多个学生。

#import <Foundation/Foundation.h>
@class Student;

@interface Teacher : NSObject
{
    NSString *name;
    //Student *student;
    NSInteger age;
    NSArray *students;
}
@end

 

4.2. 我们再给Student类中新增score 学分

#import <Foundation/Foundation.h>

@interface Student : NSObject
{
    NSString *studentName;
    NSInteger score;
}
@end

 

4.2. 在main函数中添加三个学生,添加到数组中,然后求学生数量,最低分,最高分,平均分,总分

#import "Student.h"
#import "Teacher.h"

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        Teacher *teacher = [[Teacher alloc] init];
        [teacher setValue:@"星星老师" forKey:@"name"];
        
        Student *student1 = [[Student alloc] init];
        [student1 setValue:@"99" forKey:@"score"];
        Student *student2 = [[Student alloc] init];
        [student2 setValue:@"97" forKey:@"score"];
        Student *student3 = [[Student alloc] init];
        [student3 setValue:@"100" forKey:@"score"];
        
        NSArray *array = [NSArray arrayWithObjects:student1,student2,student3,nil];
        [teacher setValue:array forKey:@"students"];
        
        
        NSLog(@"这个集合是%@", [teacher valueForKeyPath:@"students.score"]);
        NSLog(@"%@一共有%@个学生",[teacher valueForKeyPath:@"name"],[teacher valueForKeyPath:@"students.@count"]);
        NSLog(@"其中最高学分:%@", [teacher valueForKeyPath:@"students.@max.score"]);
        NSLog(@"最低学分:%@", [teacher valueForKeyPath:@"students.@min.score"]);
        NSLog(@"平均学分:%@", [teacher valueForKeyPath:@"students.@avg.score"]);
        NSLog(@"学分总和是%@",[teacher valueForKeyPath:@"students.@sum.score"]);
        /*
               
                print result is:
         这个集合是(
         99,
         96,
         100
         )
         星星老师共3个学生
         其中最高学分:100
         最低学分:96
         平均学分:98.666666666666666666666666666666666666
         学分总和是295
         */
    }
    return 0;
}

 

5、操作字典NSDictionary

知识点:[obj setValuesForKeysWithDictionary];

5.1. 我们可以通过一个NSDictionary来给一个对象初始化值  

#import "Student.h"
#import "Teacher.h"

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        
        NSDictionary *dictStudent = @{@"studentName":@"灰灰",@"score":@100};
        NSDictionary *dictTeacher = @{@"name":@"星星老师",@"age":@18,@"student":dictStudent};
        
        Teacher *teacher = [[Teacher alloc] init];
        [teacher setValuesForKeysWithDictionary:dictTeacher];
        
        NSString *name = [teacher valueForKey:@"name"];
        NSString *age = [teacher valueForKey:@"age"];
        NSString *studentName = [teacher valueForKeyPath:@"student.studentName"];
        NSString *score = [teacher valueForKeyPath:@"student.score"];
        NSLog(@"世界上最帅的老师是:%@, 他永远%@岁",name ,age);
        NSLog(@"%@得了%@分",studentName,score);
        // print result is
        // 世界上最帅的老师是:星星老师, 他永远18岁
        // 灰灰得了100分
    }
    return 0;
}

 

5.2. 但是该Dictionary 中的key值一定要与对象中的字段(属性)名称一致,数量可以少,但是不能多,否则仍然会报

“this class is not key value coding-compliant for the key otherName ” 该错误

5.3. 值的一提的是,该方法还是有缺陷的,该方法并不能类型推导出NSArray中存是什么类型的对象,所以多数存在subArray的情况,我们会选择多层KVC嵌套,或者直接使用for循环来为该对象赋值。

 

6、KVC在Swift中的差异

在Swift中,KVC的用法又会有所不同

比如说

var students:[Student]

Swift的array对象不再拥有KVC的方法

所以我们不能通过

var studentsName:[String] = students.valueForKeyPath("studentName");

来取出一个集合的纵向数据

 

当然也并不是没有解决方案,我们可以通过

var studentsName:[String] = self.valueForKeyPath("students.studentName")

这便是Swift 中KVC的一点点小差异

更多内容有待更新(老师忙……)

 

 

posted @ 2015-03-16 11:20  星星Star&#128523;  阅读(332)  评论(0编辑  收藏  举报