KVC和KVO介绍

作者: 小白进城 | 来源:发表于2017-08-14 19:37 被阅读123次

概述

1、KVC:键值编码,使用字符串的方式管理对象的成员、属性
2、KVO:键值监听,一种观察者模式,监听属性的改变,可实现UI和数据模型的分离


键值编码KVC(NSKeyValueCoding)

作用:动态管理对象属性的读写操作。

KVC的操作方法有是由NSKeyValueCoding协议提供,NSObject实现了这个协议,这意味着OC中几乎所有的对象都支持KVC操作。

使用方式:

  • 简单路径

设值:[对象 setValue:属性值 forKey:属性名]
取值:[对象 valueForKey:属性名]

  • 复合路径

设值:[对象 setValue:属性值 forKeyPath:属性路径]
取值:[对象 valueForKeyPath:属性路径]


示例:

我们定义一个Person类,并声明一个name的属性

Person.h文件

#import <Foundation/Foundation.h>
@interface Person : NSObject
@property (copy ,nonatomic) NSString *name;
@end

我们在主程序中使用KVC来控制name的取、设值操作

#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *person = [Person new];
        [person setValue:@"LOLITA" forKey:@"name"];
        NSLog(@"-->%@",[person valueForKey:@"name"]); 
    }
    return 0;
}

运行结果

取值name

事实上,KVC不仅可以设置person的属性,person的成员变量也可以操作,不管是公有还是私有

我们给Person类新增成员变量

Person.h

#import <Foundation/Foundation.h>
@interface Person : NSObject
{
    @private
    NSString *_sex;
    @public
    CGFloat _height;
}
@property (copy ,nonatomic) NSString *name;
@end

主程序中

#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *person = [Person new];
        [person setValue:@"fale" forKey:@"sex"];
        NSLog(@"-->%@",[person valueForKey:@"sex"]);
        [person setValue:@"170.0" forKey:@"_height"];
        NSLog(@"-->%@",[person valueForKey:@"height"]);   
    }
    return 0;
}

运行结果

sex和height

我们可以看到,Person的成员变量是_sex和_height,设值和取值的时候是否带"_"效果都是一样的,这跟KVC设置的机制有关

  • 设值:优先寻找setter方法,如果没有该方法则寻找成员变量_a,如果仍然不存在,则寻找成员变量a,如果还是没找到则会调用这个类的setValue:forUndefinedKey:方法,并且不管这些方法、成员变量是私有还是公有的甚至是只读的都可以正确设置;

优先级为:setter方法-->_a-->a-->setValue:forUndefinedKey:方法

  • 取值:优先寻找getter方法,如果没有找到该方法则寻找成员变量_a,如果仍然不存在,则寻找成员变量a,如果还是没有找到则会待用这个类valueforUndefinedKey:方法

优先级为:getter方法-->_a-->a-->valueforUndefinedKey:方法


补充:复合路径

如果Person中有一个Accont类,表示账户余额,要怎么使用KVC呢?

Account.h文件

#import <Foundation/Foundation.h>
@interface Account : NSObject
{
    float _balance; // 账户余额
}
@end

Person.h文件

#import <Foundation/Foundation.h>
#import "Account.h"
@interface Person : NSObject
@property (strong ,nonatomic) Account *account; // 账户余额
@end

Person.m文件

#import "Person.h"
@implementation Person
-(instancetype)init{
    if (self = [super init]) {
        self.account = [Account new];  // 需要初始化该对象
    }
    return self;
}
@end

主程序中

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

int main(int argc, const char * argv[]) {
    @autoreleasepool {
    
        Person *person = [Person new];
        [person setValue:@"1234.6" forKeyPath:@"account.balance"];
        NSLog(@"-->%@",[person valueForKeyPath:@"account.balance"]);
        
    }
    return 0;
}

运行结果:

复合路径使用


键值监听KVO(NSKeyValueObserving)

作用:实现UI和数据模型的分离

KVO其实是一种观察者模式,可以监听某对象的属性值的变化,当该属性值发生变化时,作为监听者就可以做出相应的响应动作,利用这一模式,我们可以在MVC模式下实现Module和View的之间的通信,即当Module发生变化时,UI作为观察者就可以发生相应变化。

使用方式:

  1. 注册成观察者: addObserver: forKeyPath: options: context:
  2. 重写监听回调方法:observeValueForKeyPath: ofObject: change: context:
  3. 注销观察者:removeObserver: forKeyPath或者removeObserver: forKeyPath: context:

示例:

这里使用控制器作为观察者,观察某个模型的属性来掩饰KVO的使用

首先我们创建一个项目,并新建一个数据模型

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface DataModule : NSObject
{
    NSString *_title;    // 标题
}
@end

在ViewController里面定义一个label来表示UI上的显示

self.label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 30)];
self.label.text = @"a value";
self.label.textAlignment = NSTextAlignmentCenter;
self.label.font = [UIFont systemFontOfSize:16];
self.label.center = self.view.center;
[self.view addSubview:self.label];

步骤一:初始化模型,并将控制器注册为该模型的观察者

self.module = [DataModule new];
[self.module addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:nil]; // 注册为观察者

步骤二:重写KVO的监听回调

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    if ([keyPath isEqualToString:@"title"]) {
        if (object == self.module) {
            self.label.text = [change objectForKey:@"new"];
        }else{
            [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
        }
    }
}

步骤三:必须手动注销观察者

-(void)dealloc{
    // 注销观察者
    [self.module removeObserver:self forKeyPath:@"title"];
}

这样,当数据模型发生变化时,我们就可以监听到,并作UI上的改变了

// 3秒之后,改变数据模型title的值
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self.module setValue:@"a new value" forKey:@"title"];
    });

运行结果

KVO示例

注意:一定要在合适的时间移除观察者



参考资料

iOS开发系列--Objective-C之KVC、KVO

相关文章

  • KVC、KVO的本质

    这篇文章介绍KVC、KVO的本质。如果你对KVC、KVO不了解,推荐先查看其用法:KVC和KVO学习笔记[http...

  • KVC-KVO

    https://github.com/leejayID/KVC-KVO 此文章详细介绍了KVC和KVO的使用,适合...

  • 探索KVC和KVO的本质

    原文链接: 探索KVC和KVO的本质 这篇文章主要介绍KVO和KVC, 机器底层是如何实现的 KVO的全称是Key...

  • KVC,KVO

    KVC , KVO KVC和KVO的区别及应用 KVC/KVO原理 1. KVC键值编码 KVC,即是指NSKey...

  • KVC、KVO

    KVC、KVO探识(一)KVO和KVO的详细使用 KVC、KVO探识(二)KVC你不知道的东西 KVC、KVO探识...

  • ios基础——KVO、KVC

    KVO和KVC常见问题: 1.KVC和KVO是什么.2.KVC和KVO的原理是什么3.KVC和KVO的使用场景4....

  • 可能碰到的iOS笔试面试题(7)--KVO-KVC

    KVC-KVO KVC的底层实现? KVO的底层实现? 什么是KVO和KVC? KVO的缺陷? KVO是一个对象能...

  • KVC和KVO介绍

    概述 1、KVC:键值编码,使用字符串的方式管理对象的成员、属性2、KVO:键值监听,一种观察者模式,监听属性的改...

  • iOS中KVC及KVO的简单理解

    一、KVC及KVO的介绍 KVC:即Key-Value-Coding,用于键值编码。KVO:即Key-Value-...

  • 说一下KVC和KVO

    本篇采用简单的例子,来介绍 iOS 中的 KVC 和 KVO 的用法和实现原理。 一、KVC 1. KVC是什么 ...

网友评论

本文标题:KVC和KVO介绍

本文链接:https://www.haomeiwen.com/subject/qgcprxtx.html