KVO 正确使用姿势进阶及底层实现

本文详细介绍了KVO(Key-Value Observing)的正确使用方法,包括基础方法、手动触发以及底层实现原理。强调了KVO在监听对象属性变化中的作用,以及在对象销毁时移除监听器的必要性,防止野指针错误。同时,讨论了KVO与Delegate、Notification的区别和使用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

你要知道的KVC、KVO、Delegate、Notification都在这里

转载请注明出处 http://blog.youkuaiyun.com/u014205968/article/details/78224820

本系列文章主要通过讲解KVC、KVO、Delegate、Notification的使用方法,来探讨KVO、Delegate、Notification的区别以及相关使用场景,本系列文章将分一下几篇文章进行讲解,读者可按需查阅。

KVO 正确使用姿势进阶及底层实现

KVO(key value observing)键值监听是我们在开发中常使用的用于监听特定对象属性值变化的方法,常用于监听数据模型的变化从而可以动态的修改对应视图。能够上述需求的方法有很多,后面要讲的DelegateNotification都可以实现,但都有各自的优缺点和适用场景,需要根据实际情况按需选择,但三者都很重要,在开发中都会使用。

KVC相同,OC在实现KVO时没有采用实现接口的方式,而是针对NSObject创建了一个类别,通过这样的方式使得NSObject的子类可以自行实现NSKeyValueObserving类别定义的相关方法,其他的如NSArrayNSSet这样的集合类也都定义了相关的类别,因此也可以对集合类型进行KVO的监听。本文主要进行KVO进阶讲解,基础知识还需读者自行查阅。

学习KVO最好的方法就是阅读官方文档:Key-Value Observing Programming Guide

KVO基础方法详解进阶

KVO常用的方法有如下几个:

/*
注册监听器
监听器对象为observer,被监听对象为消息的发送者即方法的调用者在回调函数中会被回传
监听的属性路径为keyPath支持点语法的嵌套
监听类型为options支持按位或来监听多个事件类型
监听上下文context主要用于在多个监听器对象监听相同keyPath时进行区分
添加监听器只会保留监听器对象的地址,不会增加引用,也不会在对象释放后置空,因此需要自己持有监听对象的强引用,该参数也会在回调函数中回传
*/
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;

/*
删除监听器
监听器对象为observer,被监听对象为消息的发送者即方法的调用者,应与addObserver方法匹配
监听的属性路径为keyPath,应与addObserver方法的keyPath匹配
监听上下文context,应与addObserver方法的context匹配
*/
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(nullable void *)context API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0));

/*
与上一个方法相同,只是少了context参数
推荐使用上一个方法,该方法由于没有传递context可能会产生异常结果
*/
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;

/*
监听器对象的监听回调方法
keyPath即为监听的属性路径
object为被监听的对象
change保存被监听的值产生的变化
context为监听上下文,由add方法回传
*/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context;

举一个简单的栗子:

#import <Foundation/Foundation.h>

@interface Account: NSObject

@property (nonatomic, copy) NSString *accountNumber;
@property (nonatomic, assign) double balance;

@end

@implementation Account

@synthesize accountNumber = _accountNumber;
@synthesize balance = _balance;

@end

@interface Person : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSUInteger age;
@property (nonatomic, strong) Account *account;

- (void)setObserver;

@end

@implementation Person

@synthesize name = _name;
@synthesize age = _age;

//添加监听器
- (void)setObserver
{
    /*
    监听器对象为Person类的对象本身,被监听的对象为Person类对象持有的account
    监听的属性路径为account的balance,可以监听嵌套的对象比如account有一个对象是bank可以监听bank是否营业,可以写"bank.isOpen"
    监听上下文设置为nil,相信很多人在使用的时候都会这么写
    */
    [self.account addObserver:self forKeyPath:@"balance" options:NSKeyValueObservingOptionNew context:nil];
}

//监听器回调方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
    //判断被监听对象是否为account,并且通过NSString来判断监听属性路径是否一致
    if (object == self.account && [keyPath isEqualToString:@"balance"])
    {
        NSLog(@
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值