iOS中KVC及KVO的简单理解

一、KVC及KVO的介绍

KVC:即Key-Value-Coding,用于键值编码。
KVO:即Key-Value-Observing,用于键值监听。

KVC:

  • 只针对类属性,设置键值对
  • 设置 setValue: forKey: ,即forKey只能为类属性
  • 取值 valueForKey

一个非正式的Protocol,提供一种机制来间接访问对象的属性。而不是通过调用Setter、Getter方法访问。
无论是Swift还是Objective-C,KVC的定义都是对NSObject的扩展来实现的(Objective-c中有个显式的NSKeyValueCoding类别名,而Swift没有,也不需要)所以对于所有继承了NSObject在类型,都能使用KVC(一些纯Swift类和结构体是不支持KVC的),下面是KVC最为重要的四个方法

- (nullable id)valueForKey:(NSString *)key;                          //直接通过Key来取值
- (void)setValue:(nullable id)value forKey:(NSString *)key;          //通过Key来设值
- (nullable id)valueForKeyPath:(NSString *)keyPath;                  //通过KeyPath来取值
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;  //通过KeyPath来设值

KVC可以在运行时动态的访问和修改对象的属性,而不是在编译时确定。允许通过Key值访问对象属性,或者给对象赋值。

KVO:
KVO是Objective-C对观察这设计模式的一种实现。(另一种是:通知机制(notification))。
KVO提供一种机制,指定一个被观察对象,当对象某个属性发生改变时,对象会获得通知。当观察某对象A时,KVO机制动态创建一个对象A当前类的子类,并为这个新的子类重写了被观察属性keyPath的setter 方法。setter 方法随后负责通知观察对象属性的改变状况。

  • 利用KVC对类属性进行设置
  • 注册observing对象** addObserver:forKeyPath:options:context:**
  • 观察者必须重写方法 observeValueForKeyPath:ofObject:change:context:

KVO是建立在KVC之上的,KVO能够观察一个对象的KVC key-path值的变化。

二、KVC及KVO的使用

<一>、kvc的使用

KVC在内部寻找key的是顺序:

  1. KVC机制,程序优先找set方法,set<key>
  2. 如果没有set方法,** accessInstanceVariablesDirectly 方法如果返回Yes,KVC会执行setValue:forUNdefinedKey:方法,找成员变量_<key>(默认返回YES)如果重写了该方法让其返回NO的话,那么在这一步KVC会执行setValue:forUNdefinedKey:**方法
  3. 如果即没有set<Key>:方法,也没有_<key>成员变量,KVC机制会搜索is<Key>的成员变量,,如果该类即没有set<Key>:方法,也没有<key>和_is<Key>成员变量,KVC机制再会继续搜索<key>和is<Key>的成员变量。再给它们赋值。
  4. 如果都没有,则会执行setValue:forUNdefinedKey:方法
#import "ViewController.h"
@interface ViewController ()
{
    NSString *testNoSetFunc;
}
@property (nonatomic, strong) NSString *testStr;//默认生成成员变量及get、set方法

@end

@implementation ViewController

//accessInstanceVariablesDirectly重写
+ (BOOL)accessInstanceVariablesDirectly{
    //默认返回YES,表示如果没有找到Set<Key>方法的话,会按照_key,_iskey,key,iskey的顺序搜索成员,设置成NO就不这样搜索

    return NO;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    NSString *test;//既没有成员变量也没有setter方法

    //fuzhi
    [self setValue:@"测试" forKey:@"test"];
    [self setValue:@"测试" forKey:@"testNoSetFunc"];
    [self setValue:@"测试" forKey:@"testStr"];
    
    //取值
    NSString *getTestNoSetFunc = [self valueForKey:@"testNoSetFunc"];
    NSString *getTest = [self valueForKey:@"test"];
    NSString *getTestStr = [self valueForKey:@"testStr"];

}

-(id)valueForUndefinedKey:(NSString *)key{
    NSLog(@"该key不存在%@",key);
    return nil;
}
//如果Key不存在,且没有KVC无法搜索到任何和Key有关的字段或者属性,则会调用这个方法,默认是抛出异常
-(void)setValue:(id)value forUndefinedKey:(NSString *)key{
    NSLog(@"该key不存在%@",key);
}

@end

如果一个类的成员变量是其他自定义类 可用valueForKeyPath 及
setValue:forKeyPath取值、赋值
valueForKeyPath
[类名 valueForKeyPath:@"类对象.类属性"];
setValue:forKeyPath:
[类名 setValue:@"值" forKeyPath:@"类对象.类属性"];

使用场景

<一>、kvc的使用

KVC是基于运行时的编程,极大的提高了灵活性,简化了代码。
(1)、动态地取值和赋值
(2)、用KVC来访问和修改私有变量
==对于类里的私有属性,Objective-C是无法直接访问的,但是KVC是可以的
(3)、Model和字典转换
(4)、修改一些控件的内部属性

    //1.添加textfield
    UITextField *textField = [[UITextField alloc]init];
    [self.view addSubview:textField];
    textField.frame = CGRectMake(10, 100, self.view.frame.size.width - 20, 30);
    //2.利用KVC修改placeholder的颜色
    textField.placeholder = @"请输入";
    [textField setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];
<二>、kvo的使用

如果赋值没有通过setter方法或者KVC,而是直接修改属性对应的成员变量,例如:仅调用_name = @"newName",这时是不会触发kvo机制,更加不会调用回调方法的。
所以使用KVO机制的前提是遵循 KVO 的属性设置方式来变更属性值。

  1. 注册观察者,实施监听;
  2. 在回调方法中处理属性发生的变化;
  3. 移除观察者
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) NSString *name;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.name = @"测试";
    //1.注册通知
    [self addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];

}
//2.监听值改变
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    
    if ([keyPath isEqualToString:@"name"]&&object == self) {
        //
        self.view.backgroundColor = [UIColor redColor];
        NSLog(@"name的新值%@",[change valueForKey:@"new"]);
        NSLog(@"name的旧值%@",[change valueForKey:@"old"]);

    }
    
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    self.name = @"更改name的值";
}
//3.移除通知
- (void)dealloc{
    [self removeObserver:self forKeyPath:@"name"];
}
@end

KVO可监听自定义类属性的变化

//testModel.h
#import <Foundation/Foundation.h>

@interface testModel : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *number;

@end

//ViewController.m
#import "ViewController.h"
#import "testModel.h"
@interface ViewController ()
@property (nonatomic, strong) testModel *model;

@end

@implementation ViewController


- (void)viewDidLoad {
    [super viewDidLoad];

    testModel *model = [[testModel alloc]init];
    self.model = model;
    model.name = @"测试";
    //1.注册通知
    [self.model addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];


}
//2.监听值改变
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    
    if ([keyPath isEqualToString:@"name"]&&object == self.model) {
        //
        self.view.backgroundColor = [UIColor redColor];
        NSLog(@"name的新值%@",[change valueForKey:@"new"]);
        NSLog(@"name的旧值%@",[change valueForKey:@"old"]);

    }
    
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    if (![self.model.name isEqualToString:@"更改name的值"]) {
        self.model.name = @"更改name的值";

    }
}
//3.移除通知
- (void)dealloc{
    [self.model removeObserver:self forKeyPath:@"name"];
}
@end
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345

推荐阅读更多精彩内容