KVO监听UI控件的frame变化,kVO的简单使用,KVC的初步了解

来源:互联网 发布:linux nfsnobody 编辑:程序博客网 时间:2024/05/17 05:07

一、KVO

KVO的是KeyValue Observe的缩写,中文是键值观察。这是一个典型的观察者模式,利用它可以很容易使用实现视图组件和数据模型的分离,当数据模型的属性值改变之后作为监听器的视图组件就会被激发,激发时就会回调监听器自身,在Objc中实现KVO则必须实现NSKeyValueObServing协议,不过幸运的是NSObject已经实现了该协议,因此几乎所哟的NSObjectd对象都可以使用KVOiOS中有个Notification的机制,也可以获得通知,但这个机制需要有个Center,相比之下KVO更加简洁而直接。

 KVO的使用也很简单,就是简单的3步。
      1.注册需要观察的对象的属性addObserver:forKeyPath:options:context:
      2.实现observeValueForKeyPath:ofObject:change:context:方法,这个方法当观察的属性变化时会自动调用
      3.取消注册观察removeObserver:forKeyPath:context:

 这里有两种方式,一种是匹配keyPath,另一种是使用context

Demo:事列

A.注册需要观察的对象

- (void)viewDidLoad{    [super viewDidLoad];        // 注册监听    [self.moveView addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];        self.p.name = @"小明";    [self.p addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:PersonAgeContext];}

B.实现

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{    // 方式1.匹配keypath    if ([keyPath isEqualToString:@"frame"]) {        NSLog(@"self.moveView.y = %f", self.moveView.y);    }        // 方式2.上下午    if (context == PersonAgeContext) {        NSLog(@"%@%d岁了", self.p.name, self.p.age);    }}

C.移除监听

-(void)dealloc // ARC模式下{    [self.moveView removeObserver:self forKeyPath:@"frame"];        [self.p removeObserver:self forKeyPath:@"age" context:PersonAgeContext];}


KVO的 优势 :

        1.能够提供一种简单的方法实现两个对象间的同步。例如:model和view之间同步;

        2.能够对非我们创建的对象,即内部对象的状态改变作出响应,而且不需要改变内部对象(SKD对象)的实现;

        3.能够提供观察的属性的最新值以及先前值;

        4.用key paths来观察属性,因此也可以观察嵌套对象;

        5.完成了对观察对象的抽象,因为不需要额外的代码来允许观察值能够被观察

       缺点 :

        1.我们观察的属性必须使用strings来定义。因此在编译器不会出现警告以及检查;

        2.对属性重构将导致我们的观察代码不再可用;

        3.复杂的“IF”语句要求对象正在观察多个值。这是因为所有的观察代码通过一个方法来指向;

        4.当释放观察者时不需要移除观察者。



二、KVC

1、KVC,即是指 NSKeyValueCoding,一个非正式的Protocol,提供一种机制来间接访问对象的属性。而不是通过调用Setter、Getter方法访问。KVO 就是基于 KVC 实现的关键技术之一。

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

  • 动态设置: setValue:属性值 forKey:属性名(用于简单路径)setValue:属性值 forKeyPath:属性路径(用于复合路径,例如Person有一个Account类型的属性,那么person.account就是一个复合属性)
  • 动态读取: valueForKey:属性名 valueForKeyPath:属性名(用于复合路径)
Demo:

Student.h

#import <Foundation/Foundation.h>@class Teacher;@interface Student : NSObject{    NSString *_nickName;}@property (nonatomic, copy) NSString *name;@property (nonatomic, strong) Teacher *teacher;@end
Student.m

#import "Student.h"#import "Teacher.h"@mplementation Student- (instancetype)init{    if (self = [super init])    {        _nickName = @"小样";            }    return self;}- (void)setTeacher:(Teacher *)teacher{    _teacher = teacher;        //注册一个KVO    //第三个参数,  NSKeyValueObservingOptionNew  获取最新改变后的值    //NSKeyValueObservingOptionOld 获取改变之前的值    //同时多个值都要传递  中间使用  |    [self.teacher addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];    /*     0x01  |  0x02     0000 0001     0000 0010     0000 0011  0x03     */}//#define KeyName @"name"//extern NSString *const  KeyName;  一般在头文件声明//static NSString *const KeyName = @"name";  在实现文件进行赋值//实现监听方法- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{    //由哪个kvo发过来的监听    if ([keyPath isEqualToString:@"name"])    {        //通过字典获取改变后的值        NSString *string = [change objectForKey:NSKeyValueChangeNewKey];        NSLog(@"newKeyPath:%@", string);    }}- (void)dealloc{    //注销监听    [self.teacher removeObserver:self forKeyPath:@"name"];        //ARC环境下重写dealloc,不要实现[super dealloc]//    [super dealloc];}

Teacher.h

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

Teacher.m

#import "Teacher.h"@implementation Teacher- (void)changeName{    _name = @"hahaha";}@end
main.m
int main(int argc, const char * argv[]) {    @autoreleasepool {                Student *student = [[Student alloc] init];        student.name = @"Jobs";                //通过kvc的方式给变量赋值        [student setValue:@"Gates" forKey:@"name"];                //KVC  key-value Coding  键值编码        NSLog(@"%@", [student valueForKey:@"name"]);                [student setValue:@"nickName" forKey:@"nickName"];        NSLog(@"%@", [student valueForKey:@"nickName"]);                NSArray *array = @[student];                //OC  动态绑定的特性        for (id obj in array)        {            NSLog(@"%@", [obj valueForKey:@"name"]);        }                //KVO   key-value observer 键值观察者        /*         1.注册一个kvo监听         2.实现监听方法         3.注销kvo监听                  MVC    可读性,可维护性,可移植性                  model          数据模型         controller     控制器         view           视图         */                //!!只有通过setter方法进行修改的时候,才能使用kvo的监听!!        Teacher *teacher = [[Teacher alloc] init];        student.teacher = teacher;                teacher.name = @"teacher1";                teacher.name = @"teacher2";                [teacher changeName];        NSLog(@"%@", teacher.name);    }    return 0;}
打印结果:

在Student类中有一个_nickName的属性,但是没有提供任何getter/setter的访问方法。同时在main.m里面有一个Student的对象指针。

       当Student实例化后,常规来说是无法访问这个对象的_nickName属性的,不过通过KVC我们做到了,在打印里有输出信息,这就说明确实读写了_nickName属性

KVC的常用方法:

- (id)valueForKey:(NSString *)key; -(void)setValue:(id)value forKey:(NSString *)key;

valueForKey的方法根据key的值读取对象的属性,setValue:forKey:是根据key的值来写对象的属性。

注意:

(1). key的值必须正确,如果拼写错误,会出现异常

(2). 当key的值是没有定义的,valueForUndefinedKey:这个方法会被调用,如果你自己写了这个方法,key的值出错就会调用到这里来

(3). 因为类key反复嵌套,所以有个keyPath的概念,keyPath就是用.号来把一个一个key链接起来,这样就可以根据这个路径访问下去

(4). NSArray/NSSet等都支持KVC



源码下载:Demo


1 0