Objective - C 經驗小談: KVC(Key Value Coding)的使用

除了:getter setter 方法,點語法, ->訪問修飾符以外,咱們還能夠用 KVC(Key-Value-Coding) 鍵值編碼來操做對象。數組

 

那麼究竟如何使用KVC操做對象的屬性呢?下面我會用詳細的例子來說解。app

一、KVC的簡單的賦值與取值函數

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

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

 

1.1.在下面的案例中,咱們設計了一個Teacher類,他繼承自NSObject,而且擁有一個名爲name的字段(屬性)。spa

#import <Foundation/Foundation.h>

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

@implementation Teacher

@end

 

1.2. 由於沒有加@property關鍵字,而且沒有加@public關鍵字,因此點語法和->訪問修飾符都沒法操做該字段,那麼怎麼辦呢?設計

      在下文的案例中,咱們使用KVC(Key Value Coding),將能爲該字段賦值,並取出打印。code

#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;
}

 

二、KVC鍵路徑訪問屬性blog

若是訪問這個類裏中的屬性中的屬性呢?

那咱們會須要用到鍵路徑: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.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;
}

 

四、操做集合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;
}

 

五、操做字典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循環來爲該對象賦值。

 

六、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的一點點小差別

更多內容有待更新(老師忙……)

相關文章
相關標籤/搜索