iOS开发之KVC/KVO

之前的文章里说到,OC主要是基于Smalltalk进行设计的,因此它有很多类似Python的动态特性,例如动态类型,动态加载,动态绑定等。今天我们将介绍OC中的键值编码(KVC)和键值监听(KVO)特性。

键值编码(KVC

       在C#我们可以反射读写一个对象的属性,有时候这种方式特别方便,因为我们可以利用字符串的方式来动态控制一个对象。因为OC的语言特性(基于Smalltalk),你根本不必进行任何操作就可以进行属性的动态读写,这种方式就是KVC(Key Value Coding)。

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

1.    动态设置:setValue:属性值 forKey:属性名(只适用于简单路径)、setValue:属性值forKeyPath:属性名路径(用于复合路径,例如HXPerson有一个HXAccount类型的属性,那么person.account就是一个复合属性)。

2.    动态读取:valueForKey:属性名valueForKeyPath:(用于复合路径)。

下面通过例子来讲解KVC

HXDog.h

#import <Foundation/Foundation.h>

@interface HXDog : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;

@end

HXDog.m

#import "HXDog.h"

@implementation HXDog

@end

HXPerson.h

#import <Foundation/Foundation.h>
@class HXDog;

@interface HXPerson : NSObject {
    @private
    int age;// 声明一个私有变量
}

@property (nonatomic, copy) NSString *name;

@property (nonatomic, strong) HXDog *dog;
/**
 *  读取该实例的私有属性
 */
- (void)showInfor;
@end

HXPerson.m

#import "HXPerson.h"

@implementation HXPerson

- (void)showInfor {
    NSLog(@"self age is: %d", age);
}

@end

在main函数中:

#import <Foundation/Foundation.h>
#import "HXPerson.h"
#import "HXDog.h"

int main(int argc, const char * argv[]) {
   HXPerson *person = [[HXPerson alloc] init];
   [person setValue:@"shx" forKey:@"name"];
   [person setValue:@28 forKey:@"age"];// 可以给私有变量设置属性值
   
   [person showInfor];// 打印私有变量的属性值
   NSLog(@"person name = %@", person.name);
   
   
   HXDog *dog = [[HXDog alloc] init];
   person.dog = dog;
   
   dog.age = 2;
   // 复合路径设置属性值
   [person setValue:@"小黄" forKeyPath:@"dog.name"];
   
   NSLog(@"---%@", [person valueForKeyPath:@"dog.name"]);
   return 0;
}

打印结果:


KVC使用起来比较简单,但是用怎样的规则对属性进行读取的呢?大概查找规则总结一下(假设现在要利用KVC对value进行读取):

1.    如果是动态设置属性,则优先考虑调用setValue方法,再考虑使用成员变量_value,再考虑使用成员变量value,在考虑使用setValue:forUndefinedKey:方法(不管这些方法、成员变量是私有的还是公共的都能正确设置))

2.    如果是动态读取属性,则优先考虑调用.value(getter方法),再考虑使用成员变量_value,再考虑使用成员变量value,在考虑使用valueForUndefinedKey:方法(不管这些方法、成员变量是私有的还是公共的都能正确读取))

键值监听(KVO

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

在OC中使用KVO操作常用的方法如下:

1.    注册指定Key路径的监听器:addObserver: forKeyPath: options: context:

2.    删除指定Key路径的监听器:removeObserver: forKeyPathremoveObserver:forKeyPath: context:

3.    回调监听:observeValueForKeyPath: ofObject: change: context:

使用KVO的具体步骤如下:

1.    通过addObserver:forKeyPath: options: context:为被监听对象(它通常是数据模型)注册监听器

2.    重写监听器的observeValueForKeyPath:ofObject: change: context:方法

还利用上面的例子,dog的age改变时,主人person要获得通知。此时dog作为被监听对象,person作为监听器对象。

HXDog.h

#import <Foundation/Foundation.h>

@interface HXDog : NSObject

@property (nonatomic, assign) int age;

@end

HXDog.m

#import "HXDog.h"

@implementation HXDog

@end

HXPerson.h

#import <Foundation/Foundation.h>
@class HXDog;

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

@property (nonatomic, strong) HXDog *dog;
@end

HXPerson.m

#import "HXPerson.h"
#import "HXDog.h"

@implementation HXPerson

- (void)setDog:(HXDog *)dog {
    _dog = dog;
    
    // 使得person成为监听对象(要重写observeValueForKeyPath方法),dog成为被监听对象
    [self.dog addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
}

/**
 *  监听器重写触发监听回调的方法
 */
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
    if ([keyPath isEqualToString:@"age"]) {// 只监听dog年龄的概念
        NSLog(@"keyPath=%@,object=%@,newValue=%.2f,context=%@",keyPath,object,[[change objectForKey:@"new"] floatValue],context);
    }
}



- (void)dealloc {
    // 移除dog被监听状态
    [self.dog removeObserver:self forKeyPath:@"age"];
}

@end

main.m

#import <Foundation/Foundation.h>
#import "HXPerson.h"
#import "HXDog.h"

int main(int argc, const char * argv[]) {
	HXPerson *person = [[HXPerson alloc] init];
    person.name = @"shxkvo";
    HXDog *dog = [[HXDog alloc] init];
    person.dog = dog;
    dog.age = 2;// 该行会触发监听器的回调方法
    dog.age = 3;// 该行会触发监听器的回调方法
	return 0;
}

打印结果:


在上面的代码中我们在给person分配dog时,就给dog的age属性添加了监听,并且在监听回调方法中输出了监听到的信息,同时在对象销毁时移除监听,这就构成了一个典型的KVO应用。










  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值