KVC与KVO

来源:互联网 发布:node co模块 阮一峰 编辑:程序博客网 时间:2024/06/06 05:19

 OC中具有很多的动态特性:例如动态类型,动态加载,动态绑定等,这里KVC(键值编码)与KVO(键值监听)也是比较常见的。


1:键值编码(KVC),用于动态属性赋值操作。

KVC的操作时由NSKeyValueCoding协议提供,NSObject对象就实现了这个协议,也就是说OC中几乎所有的对象都支持KVC操作,常用的KVC操作方法如下:

(1):

/**

 KVC的一般操作用于简单路径


 @param value 要设置的对象value

 @param key 要设置的对象的key

 */

- (void)setValue:(nullableid)value forKey:(NSString *)key;


(2):

/**

 KVC的常用方法二(用于复合路径)

给某个自定义对象的某个属性赋值就是复合路径

 @param value 要设置的对象value

 @param keyPath 要设置的对象的的属性路径

 */

-(void)setValue:(nullableid)value forKeyPath:(NSString *)keyPath;


下面通过一个例子理解KVC


//

//  info.h

//  KVC

//

//  Created by MiHu on 2016/12/8.

//  Copyright © 2016 MiHu. All rights reserved.

//


#import <Foundation/Foundation.h>


@interface info : NSObject


/**

 属性:成绩

 */

@property(nonatomic,assign)NSInteger score;


@end



//

//  info.m

//  KVC

//

//  Created by MiHu on 2016/12/8.

//  Copyright © 2016 MiHu. All rights reserved.

//


#import "info.h"


@implementation info


@end



//  person.h

//  KVC

//

//  Created by MiHu on 2016/12/8.

//  Copyright © 2016 MiHu. All rights reserved.

//


#import <Foundation/Foundation.h>



@class info;



@interface person : NSObject{

    

@private

    int _age;


}


/**

 属性:姓名

 */

@property(nonatomic,strong)NSString *name;


/**

 属性:info

 */

@property(nonatomic,strong)info * info;


/**

 公共方法

 */

-(void)showMessage;


@end


//  person.m

//  KVC

//

//  Created by MiHu on 2016/12/8.

//  Copyright © 2016 MiHu. All rights reserved.

//


#import "person.h"


@implementation person


-(void)showMessage

{

    NSLog(@"name=%@,age=%d",_name,_age);

}


@end


//  ViewController.m

//  KVC

//

//  Created by MiHu on 2016/12/8.

//  Copyright © 2016 MiHu. All rights reserved.

//


#import "ViewController.h"


#import "info.h"


#import "person.h"


@interface ViewController ()


@end


@implementation ViewController


- (void)viewDidLoad {

    [superviewDidLoad];

    

    person * per = [[personalloc]init];

    

    [per setValue:@"jac"forKey:@"name"];

    [per setValue:@20forKey:@"age"];//注意即使是私有变量也能访问

    

    [per showMessage];

    //输出:name=jac,age=20

    

    

    info * Info = [[infoalloc]init];

    

    per.info = Info;//注意这一步一定要先给account属性赋值,否则下面按路径赋值无法成功,因为accountnil,当然这一步骤也可以写成:[per setValue:account1 forKeyPath:@"account"];

    

    [per setValue:@100forKeyPath:@"info.score"];

    

    NSLog(@"per's balance is :%.2f",[[pervalueForKeyPath:@"info.score"]floatValue]);

    //输出:per's balance is :100.00

    

}



@end


KVC使用起来比较简单,但是它如何查找一个属性进行读取呢?具体查找规则(假设现在要利用KVCa进行读取):

如果是动态设置属性,则优先考虑调用setA方法,如果没有该方法则优先考虑搜索成员变量_a,如果仍然不存在则搜索成员变量a,如果最后仍然没搜索到则会调用这个类的setValue:forUndefinedKey:方法(注意搜索过程中不管这些方法、成员变量是私有的还是公共的都能正确设置)

如果是动态读取属性,则优先考虑调用a方法(属性agetter方法),如果没有搜索到则会优先搜索成员变量_a,如果仍然不存在则搜索成员变量a,如果最后仍然没搜索到则会调用这个类的valueforUndefinedKey:方法(注意搜索过程中不管这些方法、成员变量是私有的还是公共的都能正确读取)


2: 键值监听KVO

KVO其实是一种观察者模式,利用它可以很容易实现视图组件和数据模型的分离,当数据模型的属性值改变之后作为监听器 的视图组件就会被激发,激发时就会回调监听器自身。在ObjC中要实现KVO则必须实现NSKeyValueObServing协议,不过幸运的是 NSObject已经实现了该协议,因此几乎所有的ObjC对象都可以使用KVO


在OC中使用KVO的一般方法为:

/**

 注册指向指定key路径的监听器


 @param observer 谁来监听

 @param keyPath key路径

 @param options NSKeyValueObservingOptions

 @param context 上下文

 */

- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullablevoid *)context;


/**

 移除指定Key路径的监听器


 @param observer 谁来监听

 @param keyPath key路径

 @param context 上下文

 */

- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(nullable void *)context NS_AVAILABLE(10_7,5_0);


/**

 回调监听:


 @param keyPath key路径

 @param object  谁发起的

 @param change  NSKeyValueChangeKey

 @param context 上下文

 */

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context



KVO的使用方法也很简单:

  1. 通过addObserver: forKeyPath: options: context:为被监听对象(它通常是数据模型)注册监听器
  2. 重写监听器的observeValueForKeyPath: ofObject: change: context:方法


继续上面的例子介绍KVO 我们希望当对象的姓名发生改变的时会回调监听方法。那么此时姓名就作为我们的被监听对象,需要Person为它注册监听(使用addObserver: forKeyPath: options: context:);而人员Person作为监听器需要重写它的observeValueForKeyPath: ofObject: change: context:方法,当监听的成绩发生改变后会回调监听器Person监听方法(observeValueForKeyPath: ofObject: change: context:)


#import "ViewController.h"


#import "info.h"


#import "person.h"



@interface ViewController()

@property(nonatomic,strong)person * per;


@end


@interface ViewController ()


@end


@implementation ViewController


- (void)viewDidLoad {

    [superviewDidLoad];

    


     _per = [[personalloc]init];

    

    info *Info = [[infoalloc]init];

    

    _per.info = Info;

    

    [_persetValue:@"jac"forKey:@"name"];

    [_persetValue:@90forKeyPath:@"info.score"];

    

    [_peraddObserver:selfforKeyPath:@"name"options:(NSKeyValueObservingOptionNew)context:nil];

    

    _per.name =@"jenny";

}



#pragma mark - 覆盖方法


#pragma mark 重写observeValueForKeyPath方法,当姓名变化后此处获得通知

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context

{

    //只处理姓名改变

    if([keyPathisEqualToString:@"name"])

    {

        NSLog(@"我改名字了");

    }

}


#pragma mark 重写销毁方法

-(void)dealloc

{

    [selfremoveObserver:selfforKeyPath:@"name"];

}







0 0