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

一、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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值