iOS——KVO

27 篇文章 0 订阅

一、概述

KVO,即:Key-Value Observing,它提供一种机制,当指定的对象的属性被修改后,则对象就会接受到通知。简单的说就是每次指定的被观察的对象的属性被修改后,KVO就会自动通知相应的观察者了。
KVO其实也是“观察者”设计模式的一种应用。我的看法是,这种模式有利于两个类间的解耦合,尤其是对于 业务逻辑 M与视图控制 C 这两个功能的解耦合。

KVO使⽤用步骤:

1、注册观察者(为被观察者指定观察者以及被观察属性)

2、实现回调⽅方法

3、触发回调⽅方法(被观察属性发⽣生更改)

4、移除观察者

KVO使⽤用场景:MVC中M与C通信,M发⽣生变化通知C。其中M是被
观察者,C是观察者。

KVO注意事项:观察者销毁之前,移除观察者,否则会出现程序异 常(给已经销毁的对象发送消息)

实例:
有一个业务类:Walker,在这个类内部只负责关于业务逻辑的处理,比如负责从服务器传来的JSON中解析数据,或做其他业务数据上的处理。
有另一个类:ViewController,专门负责界面的交互与试图更新。其中,需要将Walker的某些属性显示出来,并实时更新。

目前,据我所能想到的方法有以下几种:

方法1、直接的函数调用

在Walker的类内部,把创建一个ViewController的对象,然后调用ViewController的修改界面的方法,把需要改动的属性值作为形参传给该函数。(加载数据之后动态刷新)

这种方式最直观,因为它不需要绕任何弯子。但是,确实最糟的方法。因为Walker与ViewController这两个类从此紧紧耦合在一起了。记住这句话,处理业务逻辑的类,对外部的事情知道得越少越好。甚至于,要做到外部是否有VC(View Controller),有多少个VC都不影响我。假设这是一个项目,程序员A负责业务逻辑的处理,程序员B负责UI,则采取这种方式后,程序员A就受制于B,互相干扰。

方法2、利用消息通信机制(NSNotification)

在Walker内部建立消息中心NSNotificationCenter,把实例化之后的VC对象作为observer。Center建在Walker或者VC都无所谓,这种方法比前面的方法好一些。但是有一个很大的缺点:如果Walker需要更改的属性很多而且很频繁,那么这种方式很不方便传值。而且,注意到了没,“把实例化后的VC对象作为observer”,始终逃不开在Walker内部对VC实例化。依旧是耦合着。

方法3、利用delegate
关于delegate的介绍有很多,这里就不多讲。但是在这种需求下用 delegate,有点“杀鸡用牛刀”感觉,成本较大,而且不直观。

方法4、利用KVO模式

所有的代码都将在ViewController中实现。对于Walker,它自己都不知道外部是否有VC,以及VC会怎用用我的属性。

代码
Person 类属性name。
Controller功能:
nameLabel.text = person.name;
点击页面实例变量person的 name 属性值发生改变。
随之实现nameLabel.text 的改变。


#import "ViewController.h"
#import "Person.h"

@interface ViewController ()
@property (nonatomic, strong) Person * person;
@property (weak, nonatomic) IBOutlet UILabel *nameLabel;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
   //初始化person 实例变量
    self.person = [[Person alloc] init];
    self.person.name = @"宋静静";
    // nameLabel.text 的值为self.person.name ,此时为宋静静。不使用观察者模式,当name 的值发生改变时,nameLabel.text显示并没有改变。
    _nameLabel.text = self.person.name;

//  添加观察者,为person 实例变量添加观察者,键值是 name 属性。也就是监测的是person的name属性。 NSKeyValueObservingOptionNew 新值
    [self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:@"描述"];
}
//点击按钮 为实现名字改变,name 被监听后,一旦name 的值改变,触发下面方法。改变label 的值
//通知是一种观察者模式

#pragma mark 触发观察者 被观察者对象的属性一旦发生变化,立即触发此方法。
-(void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
//    keyPath 对象被观察的属性。
//    object :被观察对象
//    change:被观察对象的属性变化. change 是一个字典。key 值为new 时,键值就是更新后的数据
//   context: 描述,可以用来传值,使用强制转换获取值。
    NSLog(@"%@",keyPath);
    NSLog(@"%@",object);
    NSLog(@"%@",change);
    NSLog(@"%@",context);
//    多个观察者存在时, 可以根据被观察对象的类别以及被观察属性做出不同操作。
    if ([object isKindOfClass:[Person class]]&&[keyPath isEqualToString:@"name"]) {
    //设置监听到 改变后需要执行的操作,这里是设置 _nameLabel.text。也可以设置背景变色(name 值修改背景变色)等等
    //        两种方式等价
    //1.  _nameLabel.text = change [@"new"];
    // 2.  _nameLabel.text = change[NSKeyValueChangeNewKey];
    //      第二种内部实现: NSString *const NSKeyValueChangeNewKey = @"new"; _nameLabel.text = change[NSKeyValueChangeNewKey];
    }
//    移除观察者 例如音乐缓存结束后,移除观察者
    [self.person removeObserver:self forKeyPath:@"name"]; 
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    self.person.name = @"邹静";
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值