Objective-C中的Key-Value Coding and Key-Value Observing(KVC与KVO)

KVC与KVO是Objective-C的关键概念,KVC指的是NSKeyValueCoding,一个非正式的协议,提供一种机制来间接访问对象的属性。KVO是一种实现KVC的关键技术之一。

一个对象拥有某些属性。比如一个Person对象有一个name和address属性,以KVC的说法,Person对象分布有一个value对应于他的name和address的key。key是一个OC字符串,它对应的值可以是任意类型的对象。从最基础的层次上看,KVC有两个方法:一个是set key的值,一个是get key的值。如下代码:

Person.h

#import <Foundation/Foundation.h>

@interface Person : NSObject

@property(nonatomic,copy)NSString *name;
@property(nonatomic,copy)NSString *address;
@property(nonatomic,strong)Person *spouse;

- (instancetype)initWithName:(NSString *)n andAddress:(NSString *)add;

- (instancetype)initWithName:(NSString *)n andAddress:(NSString *)add andSpouse:(Person *)p;

@end

Person.m

#import "Person.h"

@implementation Person

- (instancetype)initWithName:(NSString *)n andAddress:(NSString *)add
{
    if(self = [super init])
    {
        self.name = n;
        self.address = add;
    }
    return self;
}

- (instancetype)initWithName:(NSString *)n andAddress:(NSString *)add andSpouse:(Person *)p
{
    if(self = [super init])
    {
        self.name = n;
        self.address = add;
        self.spouse = p;
    }
    return self;
}

@end

main.m

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

void changeName(Person *p,NSString *newName)
{
    NSString *oriName = [p valueForKey:@"name"];
    [p setValue:newName forKey:@"name"];
    NSLog(@"Changed %@'s name to %@.",oriName,newName);
}

void logMarriage(Person *p)
{
    NSString *personName = [p valueForKey:@"name"];
    NSString *spouseName = [p valueForKeyPath:@"spouse.name"];

    NSLog(@"%@ is merried to %@.",personName,spouseName);
}

int main(int argc, const char * argv[]) {

    Person *spouse = [[Person alloc] initWithName:@"小雅" andAddress:@"河北"];
    Person *p = [[Person alloc] initWithName:@"小明" andAddress:@"北京" andSpouse:spouse];
    changeName(p,@"小李");
    NSLog(@"%@", [p name]);

    logMarriage(p);

    return 0;
}

主函数中的changeName函数是以KVC的方式用newName替换[p valueForKey:@”name”]
现在如果Person *p有一个配偶spouse,spouse是Person类的一个对象,用KVC的方法实现logMarriage函数,其中

 NSString *spouseName = [p valueForKeyPath:@"spouse.name"];

valueForKeyPath指的是找到spouse对象的name。key与key path要区分开来,key可以从一个对象中获取值,而key path可以将对个key用”.”号分割连接起来。我们的代码中:

 NSString *spouseName = [p valueForKeyPath:@"spouse.name"];
 /*相当于是
 NSString *spouseName = [[p valueForKey:@"spouse"] valueForKey:@"name"];
 */

Key-Value Observing(KVO)指的是建立在KVC上,能够观察一个对象的KVC key path的值的变化。比如说,现在一个person对象的address发生变化,我们可以用代码进行观察,以下是3种实现的方法:

  • watchPersonForChangeOfAddress:实现观察
  • observeValueForKeyPath: ofObject: change: context:在被观察的key path变化时调用
  • dealloc 停止观察

在项目中添加一个类。
PersonWatcher.h

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

@interface PersonWatcher : NSObject

- (void)watchPersonForChangeOfAddress:(Person *)p;

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary<NSKeyValueChangeKey,id> *)change
                       context:(void *)context;
@property(nonatomic,copy)NSMutableArray *m_observedPeople;

@end

PersonWatcher.m

#import "PersonWatcher.h"

static NSString *const KVO_CONTEXT_ADDRESS_CHANGED = @"KVO_CONTEXT_ADDRESS_CHANGED";
@implementation PersonWatcher

- (id)init
{
    if(self = [super init])
        _m_observedPeople = [NSMutableArray new];
    return self;
}

- (void)dealloc
{
    for(id p in _m_observedPeople)
        [p removeObserver:self forKeyPath:@"address"];
    _m_observedPeople = nil;
}

- (void)watchPersonForChangeOfAddress:(Person *)p
{
    [p addObserver:self forKeyPath:@"address" options:0 context:(__bridge void * _Nullable)(KVO_CONTEXT_ADDRESS_CHANGED)];
    [_m_observedPeople addObject:p];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
    if(context == (__bridge void *)(KVO_CONTEXT_ADDRESS_CHANGED))
    {
        NSString *name = [object valueForKey:@"name"];
        NSString *address = [object valueForKey:@"address"];
        NSLog(@"%@ has a new address: %@",name,address);
    }
}

@end

主函数中添加如下几行代码:

    PersonWatcher *pw = [PersonWatcher new];
    [pw watchPersonForChangeOfAddress:p];

    p.address = @"辽宁";
    p.address = @"山东";

这个时候,当一个Person对象的地址属性发生改变的时候,PersonWatcher会通知我们。
这里写图片描述

以上就是KVC与KVO的全部内容。
参考自:Cocoa Programming for Mac OS X 4th Edition.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值