KVC 和 KVO

一. KVC

1.KVC介绍

    KVC 就是键值编码(key-value-coding)。

2.KVC 的主要作用:

   (1)通过键值路径为对象的属性赋值。主要是可以为私有的属性赋值。

    AppleViewController *appleVC = [[AppleViewController alloc]init];
    [appleVC setValue:@"橘子" forKey:@"name"];
如果对象A的属性是一个对象B,要设置对象B的属性
    [person setValue:@"旺财" forKeyPath:@"dog.name"];

(2)通过键值路径获取属性的值。主要是可以通过key获得私有属性的值。

    NSString *nameStr = [appleVC valueForKey:@"name"];

   也可以通过keypath获得值

    NSString *dName = [person valueForKeyPath:@"dog.name"];

(3)将字典转型成Model,方法:setValuesForKeysWithDictionary:

复制代码
    // 定义一个字典
    NSDictionary *dict = @{
                           @"name"  : @"jack",
                           @"money" : @"20.7",
                           };
    // 创建模型
    Person *p = [[Person alloc] init];
    
    // 字典转模型
    [p setValuesForKeysWithDictionary:dict];
    NSLog(@"person's name is the %@",p.name);
复制代码

注意:字典的key和Model的属性一定要一一对应。否则会出现错误。比如person里没有name的属性,系统报错如下:

'[<Person 0x60000001d0b0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key name.'

(4)常用方法
   它提供了一种使用字符串而不是访问器方法去访问一个对象实例变量的机制。
    
    1.通过key(成员变量的名称)设置value(成员变量的值)
    - (void)setValue:(id)value forKey:(NSString *)key;

    2.通过keyPath(成员变量的路径)设置value(成员变量的值)
    - (void)setValue:(id)value forKeyPath:(NSString *)keyPath;

    3.通过key(成员变量的名称)获取value(成员变量的值)
    - (id)valueForKey:(NSString *)key;

    4.通过keyPath(成员变量的路径)获取value(成员变量的值)
    - (id)valueForKeyPath:(NSString *)keyPath;

    5.重写此方法防止出现未定义key值的时候出现崩溃
    - (void)setValue:(id)value forUndefinedKey:(NSString *)key;

    6.重写此方法防止获取未定义key值的时候出现崩溃
    - (id)valueForUndefinedKey:(NSString *)key;

    7.通过键值对的形式给成员变量赋值
    - (void)setValuesForKeysWithDictionary:(NSDictionary *)keyedValues;

举个简单的例子:一个对象拥有某些属性。比如说,一个 Person 对象有一个 perName,uid,perID和一个 sex 属性。以 KVC 说法,Person 对象分别有一个 value 对应他的 perName,uid,perID和一个 sex 的 key。 key 只是一个字符串,它对应的值可以是任意类型的对象。从最基础的层次上看,KVC 有两个方法:一个是设置 key 的值,另一个是获取 key 的值。

#import <Foundation/Foundation.h>

@interface Person : NSObject

@property (nonatomic,copy)NSString *perName;
@property (nonatomic,copy)NSString *sex;
@property (nonatomic,copy)NSString *uid;
@property (nonatomic,copy)NSString *perID;


@end
#import "Person.h"

@implementation Person

-(void)setValue:(id)value forKey:(NSString *)key{
    
    //判断是否为NSNumber类型
    if ([value isKindOfClass:[NSNumber class]]) {
        
        [self setValue:[NSString stringWithFormat:@"%@",value] forKey:key];
        
    }else {
        
        //调用父类的方法
        [super setValue:value forKey:key];
        
    }
    
}

-(void)setValue:(id)value forUndefinedKey:(NSString *)key{
    //手动赋值
    if ([key isEqualToString:@"id"]) {
        
        [self setValue:value forKey:@"perID"];
        
    }else{
        
        NSLog(@"未定的key值:%@",key);
        
    }
     
}

-(id)valueForUndefinedKey:(NSString *)key{
    
    NSLog(@"未找到key:%@",key);
    return nil;
}

- (NSString *)description
{
    return [NSString stringWithFormat:@"name = %@,sex = %@,ID= %@,perID = %@", _perName,_sex,_uid,_perID];
}

@end
#import <Foundation/Foundation.h>
#import "Person.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
      
        Person *per = [Person new];
        
        //模拟返回的服务器数据
        NSDictionary *dictionary = @{@"perName":@"小红",@"sex":@"女",@"uid":[NSNumber numberWithInteger:888],@"id":@"12345"};
        
        
        //通过KVC一次性给3个成变量赋值
        [per setValuesForKeysWithDictionary:dictionary];
        
        NSLog(@" %@",per);
        
    }
    return 0;
}


========================================

二. KVO  

1.KVO介绍:

     KVO 是键值观察者(key-value-observing)。KVO提供了一种观察者的机制,通过对某个对象的某个属性添加观察者,当该属性改变,就会调用"observeValueForKeyPath:"方法,为我们提供一个“对象值改变了!”的时机进行一些操作。

2.KVO原理

  当某个类的对象第一次被观察时,系统在运行时会创建该类的派生类,改派生类中重写了该对象的setter方法,并且在setter方法中实现了通知的机制。派生类重写了class方法,以“欺骗”外部调用者他就是原先那个类。系统将这个类的isa指针指向新的派生类,因此改对象也就是新的派生类的对象了。因而改对象调用setter就会调用重写的setter,从而激活键值通知机制。此外派生类还重写了delloc方法来释放资源。

3.KVO的使用

     (1)给对象的属性添加观察者  

    [appleVC addObserver:self forKeyPath:@"name" options: NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:NULL];

        注: options: NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld 返回未改变之前的值和改变之后的值    context可以为空

  (2)若该属性发生改变,系统自动调用下面的方法:

复制代码
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
    //拿到新值/旧值,进行操作
    NSLog(@"newValue----%@",change[@"new"]);
    NSLog(@"oldValue----%@",change[@"old"]);

}
复制代码

  (3)取消监听

-(void)dealloc
{
    [person removeObserver:self forKeyPath:@"test"];
}

4.KVO的使用场景

KVO用于监听对象属性的改变。

  (1)下拉刷新、下拉加载监听UIScrollView的contentoffsize;
  (2)webview混排监听contentsize;
  (3)监听模型属性实时更新UI;
  (4)监听控制器frame改变,实现抽屉效果。

(5)方法和举例
 提供了一种当其它对象属性被修改的时候能通知当前对象的机制

    1.添加观察者
    - (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context;

    2.对象被释放之前,要移除观察者,通常写在dealloc函数当中
    - (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;

    3.当属性值发生变化之后的回调函数
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;

监听一个contentSize的例子:点击一个加号会添加10个cell

#import "ViewController.h"

@interface ViewController ()<UITableViewDataSource,UITableViewDelegate>

@property (nonatomic,assign)NSInteger count;//控制行数
@property (nonatomic,assign)CGSize recordSize;//记录当前UITableView的ContentSize;
@property (weak, nonatomic) IBOutlet UITableView *myTableView;

@end

@implementation ViewController



-(void)dealloc{
    
    //移除观察者
    [self.myTableView removeObserver:self forKeyPath:@"contentSize"];
    
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //观察myTableView的contentSize
    [self.myTableView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];
    
    //设置导航栏右侧按钮
    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"+" style:UIBarButtonItemStylePlain target:self action:@selector(add)];
    
    self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"__" style:UIBarButtonItemStylePlain target:self action:nil];
    
}

//增加行数
-(void)add{
    
    self.count += 10;
    
    //刷新UI
    [self.myTableView reloadData];
    //self.recordSize = self.myTableView.contentSize;
    
    //NSLog(@"%f",self.recordSize.height);
}
#pragma mark- KVO回调方法
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    
    //判断是否为对应的观者属性
    if ([keyPath isEqualToString:@"contentSize"] && object == self.myTableView) {
        
        NSLog(@"%@ %f",change, self.recordSize.height);
        
        //记录变化之后的contentSize
        self.recordSize = [change[@"new"] CGSizeValue];
        
    }
    
    
}

#pragma mark- UITableView代理
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
    
    return self.count;
}

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    
    return 1;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
    
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
    }
    
    cell.textLabel.text = @"哈哈";
    
    return cell;
    
}


@end

 如果对你有帮助,请关注我哦!

原文地址:https://www.cnblogs.com/laolitou-ping/p/6256486.html