KVC和KVO

一、KVC:鍵值編碼

1、創建三個類:Person、Car 和 JsonModel

Car類的實現部分如下:

#import "Car.h"
@interface Car()
@property (nonatomic, copy) NSString * carName;
@property (nonatomic, assign)int carPrice;
@end

@implementation Car

@end

Person類的實現部分如下:

#import "Person.h"
#import "Car.h"
@interface Person()
@property (nonatomic, copy) NSString * name;
@property (nonatomic, copy) NSString* pass;
@property (nonatomic, strong) NSDate * birth;
@property (nonatomic, copy,readonly) NSString * readName;
@property (nonatomic,assign) int price;
@property (nonatomic, strong) Car * car;//包含一個Car的實例對象
@end
@implementation Person


//對不存在的key賦值調用此方法
-(void)setValue:(id)value forUndefinedKey:(NSString *)key{
    NSLog(@"value = %@",value);
    NSLog(@"key = %@",key);
    if ([key isEqualToString:@"noExist"]) {
        NSLog(@"對不存在的key進行處理");
    }
}

//對不存在的key取值調用此方法
-(id)valueForUndefinedKey:(NSString *)key{
    NSLog(@"key = %@",key);
    if ([key isEqualToString:@"noExist"]) {
        NSLog(@"對不存在的key進行處理");
    }
    return nil;
}
//處理value爲nil時調用此方法(比如給int 類型賦值nil就會調用此方法)
-(void)setNilValueForKey:(NSString *)key{
    //對price進行處理
    if ([key isEqualToString:@"price"]) {
        key = 0;
    }
    else{
        [super setNilValueForKey:key];
    }
}

@end

JsonModel類的接口部分如下:

#import <Foundation/Foundation.h>

@interface JsonModel : NSObject
@property (nonatomic, strong) NSString * name;
@property (nonatomic, assign) int age;
@property (nonatomic, strong) NSArray * cars;
@end

ViewController類的實現部分:

#import "ViewController.h"
#import "Person.h"
#import "Car.h"
#import "JsonModel.h"
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    Person * person = [[Person alloc] init];
    //person.name = @"大聖";//私有屬性是無法賦值的
    //1、給私有屬性賦值和取值
    [person setValue:@"大聖" forKey:@"name"];
    [person setValue:@"1455" forKey:@"pass"];
    [person setValue:[[NSDate alloc] init] forKey:@"birth"];
    NSLog(@"person.name = %@",[person valueForKey:@"name"]);
    NSLog(@"person.path = %@",[person valueForKey:@"pass"]);
    NSLog(@"person.birth = %@",[person valueForKey:@"birth"]);
    
    //person.readName = @"read";//只讀屬性是無法賦值的
    //2、給只讀屬性賦值和取值
    [person setValue:@"read" forKey:@"readName"];
    NSLog(@"readName = %@",[person valueForKey:@"readName"]);
    
    //3、給不存在的屬性賦值和取值
    [person setValue:@"noExistValue" forKey:@"noExist"];
    NSLog(@"noExist = %@",[person valueForKey:@"noExist"]);
    
    //4、當value爲nil時如何處理
    [person setValue:nil forKey:@"price"];
    NSLog(@"price = %@",[person valueForKey:@"price"]);
    
    //5、給可變字典賦值和取值
    NSMutableDictionary * dic = [[NSMutableDictionary alloc] init];
    [dic setValue:@"value1" forKey:@"key1"];// 類似於[dict setObject:@"value1" forKey:@"key1"];
    [dic setValue:@"value2" forKey:@"key2"];
    NSLog(@"key1的Value值爲 = %@",[dic valueForKey:@"key1"]);// 類似於 [dict objectForKey:@"key1”];
    NSLog(@"key2的Value值爲 = %@",[dic valueForKey:@"key2"]);
    
    //6、用字典給一個模型賦值,json解析時可用
    NSDictionary * jsonDict = @{
                                @"name":@"Jack",
                                @"age":@18,
                                @"cars":@[@"BMW",@"Benz"]
                                };
    JsonModel * jsonModel = [[JsonModel alloc] init];
    [jsonModel setValuesForKeysWithDictionary:jsonDict];
    NSLog(@"jsonModel.name = %@",jsonModel.name);
    NSLog(@"jsonModel.age = %d",jsonModel.age);
    NSLog(@"jsonModel.cars = %@",jsonModel.cars);
    
    //7-1、key路徑給複合屬性賦值和取值
    [person setValue:[[Car alloc] init] forKey:@"car"];//只有給car實例化後才能給car的屬性賦值
    [person setValue:@"BMW" forKeyPath:@"car.carName"];//通過key路徑給car.carName賦值
    NSLog(@"car.name = %@",[person valueForKeyPath:@"car.carName"]);//通過key路徑對car.carName取值。
    
    //7-2、key路徑與字典
    NSArray *arr = @[
                     @{
                         @"city":@"beijing",
                         @"person":@{
                                        @"name":@"zhangsan",
                                        @"city":@"guangzhou"
                                    }
                      },
                     
                     @{
                         @"city":@"chengdu"
                      }
                     
                     ];
    NSArray * arrCitys = [arr valueForKeyPath:@"city"] ;
    //可以獲取到arrCitys數組 @[@"beijing",@"guangzhou",@"chengdu"]
    NSLog(@"arrCitys = %@",arrCitys);
    
    //7-3、key路徑與數組
    NSArray * array = @[@1,@2,@3,@4,@5];
    CGFloat sum = [[array valueForKeyPath:@"@sum.floatValue"] floatValue];//求和
    CGFloat avg = [[array valueForKeyPath:@"@avg.floatValue"] floatValue];//平均值
    CGFloat max =[[array valueForKeyPath:@"@max.floatValue"] floatValue];//最大值
    CGFloat min =[[array valueForKeyPath:@"@min.floatValue"] floatValue];//最小值
    NSLog(@"sum = %f,avg = %f,max = %f,min = %f",sum,avg,max,min);
    
    
    /*
    PS:
     以Person類的name屬性爲例
     1、程序優先考慮調用setName:方法來進行賦值
     2、如果沒有setName:方法會搜索名稱爲_name成員變量,不論該成員變量是在接口部分還是實現部分也不論用什麼控制符修飾
     3、如果_name不存在,就會搜索名稱爲name的成員變量,不論該成員變量是在接口部分還是實現部分也不論用什麼控制符修飾
     4、如果上述三條都不滿足就會調用setValue:forUndefineKey:方法,要想對不存在的屬性進行處理在Person類內的實現部分重寫此方法
     */
    
    /*
     PS:
     以name屬性爲例
     1、程序優先考慮調用name:方法來進行賦值
     2、如果沒有name:方法會搜索名稱爲_name成員變量,不論該成員變量是在接口部分還是實現部分也不論用什麼控制符修飾
     3、如果_name不存在,就會搜索名稱爲name的成員變量,不論該成員變量是在接口部分還是實現部分也不論用什麼控制符修飾
     4、如果上述三條都不滿足就會調用valueforUndefineKey:方法,要想對不存在的屬性進行處理在Person類內的實現部分重寫此方法
     */
}


二、KVO:鍵值監聽

將Person類的car屬性移到接口文件,

在Car類的接口文件增加NSString * color;和NSString * speed;兩個屬性

在Person類的實現文件內增加如下方法

//設置person.car的時候就給car添加鍵值觀察者
-(void)setCar:(Car *)car{
    _car = car;
    //新值舊值一起觀察,即可以將新的value和就的value都傳到change數組,這樣在調用方法中就可以拿到這兩個值
    [_car addObserver:self forKeyPath:@"color" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
    [_car addObserver:self forKeyPath:@"speed" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
}
//只要person.car.color屬性或者person.car.speed屬性發生改變就會調用這個方法
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{
    NSLog(@"調用了觀察者方法");
    NSLog(@"keyPath = %@",keyPath);
    NSLog(@"object = %@",object);
    NSLog(@"change = %@",change);
    NSLog(@"new = %@",change[@"new"]);
    NSLog(@"old = %@",[change objectForKey:@"old"]);
    NSLog(@"context = %@",context);
}

- (void)dealloc
{
    //移除監聽者
    [self.car removeObserver:self forKeyPath:@"color"];
    [self.car removeObserver:self forKeyPath:@"speed"];
}

在ViewController.m文件內增加如下方法

//創建person實例及person.car實例並賦值
-(void)kvoTest{
    [self createButton];
    Car * car = [[Car alloc] init];
    self.car = car;
    car.color = @"white";
    Person * person = [[Person alloc] init];
    person.car = car;//此行代碼會爲person對象的car屬性添加觀察者
    self.person = person;
}
//創建button
-(void)createButton{
    UIButton * myBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    myBtn.frame = CGRectMake(40, 80, 100, 50);
    [myBtn setTitle:@"按鈕" forState:UIControlStateNormal];
    myBtn.backgroundColor = [UIColor blueColor];
    [myBtn addTarget:self action:@selector(clickBtn:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:myBtn];
}
//button的點擊事件
-(void)clickBtn:(UIButton*)btn{
    //只要person.car的屬性發生改變就會調用觀察者方法
    self.person.car.color = @"black";
    self.person.car.speed = 200;
    
}


參考文獻:

1、KVC/KVO原理詳解及編程指南 - 王中周的個人博客 - 博客頻道 - CSDN.NET

2、你不知道的valueForKeyPath - 簡書



發佈了126 篇原創文章 · 獲贊 8 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章