iOS Swift && Objective-CiOS开发深度好文iOS开发

KVC和KVO介绍

2017-08-14  本文已影响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:方法

优先级为: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

上一篇 下一篇

猜你喜欢

热点阅读