iOS开发基础15-KVO的应用与底层逻辑
在 iOS 开发中,Key-Value Observing
(KVO) 是一种 powerful 的机制,用于监听对象属性的变化。通过 KVO,可以在属性值发生改变时接收通知,从而实现响应式编程风格的开发。这篇文章将详细介绍如何使用 KVO 监听对象属性的变化,并分析其底层工作原理。
一、KVO 的使用
1. 基本使用方法
下面我们通过一个简单的示例展示如何使用 KVO 来监听对象属性的变化。
首先定义 Person
类:
Person.h
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
@end
Person.m
#import "Person.h"
@implementation Person
@end
2. 监听属性变化
接下来,在我们的视图控制器中添加对 age
属性的监听。
ViewController.m
#import "ViewController.h"
#import "Person.h"
@interface ViewController ()
@property (nonatomic, strong) Person *person;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.person = [[Person alloc] init];
self.person.name = @"chg";
self.person.age = 30;
// 给 self.person 对象添加 KVO 监听
[self.person addObserver:self
forKeyPath:@"age"
options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew
context:nil];
}
// 实现 KVO 回调方法
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey,id> *)change
context:(void *)context {
if ([keyPath isEqualToString:@"age"]) {
NSLog(@"Age changed from %@ to %@", change[NSKeyValueChangeOldKey], change[NSKeyValueChangeNewKey]);
}
}
// 移除监听
- (void)dealloc {
[self.person removeObserver:self forKeyPath:@"age"];
}
@end
3. 注意事项
- 移除监听:当对象释放之前,一定要移除监听,否则会导致程序崩溃,并提示类似于 "An instance of class Person was deallocated while key value observers were still registered with it."
- 只能监听通过 setter 方法修改的属性:KVO 只能监听通过 KVC 或 setter 方法修改的属性,直接修改实例变量不会触发 KVO 通知。
二、KVO 工作原理
1. 动态子类与 Method Swizzling
当我们为对象注册一个 KVO 监听时,系统会在运行时动态地创建该对象的一个子类,并重写被监听属性的 setter 方法。这个子类会拦截属性的 setter 方法,并在值改变时调用 willChangeValueForKey:
和 didChangeValueForKey:
方法来通知所有的监听者。
例如,假设我们有一个 Person
类的实例 p
,当我们调用 [p addObserver:forKeyPath:options:context:]
方法时,系统会生成一个 NSKVONotifying_Person
类作为 Person
类的动态子类。这个子类会重写 setAge:
方法,并在方法中包含代码来通知监听器。
2. KVO 的回调方法
当监听的属性值发生变化时,KVO 会调用监听者的 observeValueForKeyPath:ofObject:change:context:
方法。我们可以在这个回调方法中处理属性变化。
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context {
if ([keyPath isEqualToString:@"age"]) {
NSLog(@"Age changed from %@ to %@", change[NSKeyValueChangeOldKey], change[NSKeyValueChangeNewKey]);
}
}
3. 实现细节
- willChangeValueForKey:在属性值即将改变时调用。
- didChangeValueForKey:在属性值已经改变时调用。
这两个方法是 KVO 实现的关键,它们通过 NSKeyValueObserving
协议在运行时动态地将属性变化与监听器联系起来。
- (void)setAge:(int)age {
[self willChangeValueForKey:@"age"];
_age = age;
[self didChangeValueForKey:@"age"];
}
三、总结
KVO 是一种强大的机制,允许我们以一种声明式编程的方式来监听对象属性的变化。通过 KVO,可以在属性值发生变化时接收通知,从而在应用程序中实现响应式编程。然而,使用 KVO 时需注意正确管理监听器的添加和移除,以避免导致潜在的内存泄漏或程序崩溃。此外,KVO 的内部实现基于运行时的 Method Swizzling
技术,使得它在提供强大功能的同时也具有一定的复杂性。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!