OC底层原理4:KVO(本质、动态生成子类、如何手动触发)

一、KVO的本质

1. 创新一个新类

利用runtime的API动态生成一个子类,并且让实例对象的isa指针指向这个全新的子类。

swift 复制代码
self.person1 = [[GSPerson alloc] init];
[self.person1 addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:@"Person.age changed"];
NSLog(@"类对象 -> %@", object_getClass(self.person1));

输出:类对象 -> NSKVONotifying_GSPerson

系统创建了一个以NSKVONotifying_开头的子类,实例对象的isa指针指向这个子类。

2. 实现逻辑

当修改实例对象的属性(调用set方法),会调用Foundation的_NSSetxxxValueAndNotify函数,函数内部会调用:

  • willChangeValueForKey:
  • super setXXX:

  • didChangeValueForKey:(方法内部会触发监听器的监听方法observeValueForKeyPath:ofObject:change:context:)
less 复制代码
NSLog(@"添加observer之前---setAge:方法的调用位置:%p", [self.person1 methodForSelector:@selector(setAge:)]);
[self.person1 addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:@"Person.age changed"];
NSLog(@"添加observer之后---setAge:方法的调用位置:%p", [self.person1 methodForSelector:@selector(setAge:)]);

输出:
添加observer之前---setAge:方法的调用位置:0x1022317e8
添加observer之后---setAge:方法的调用位置:0x1078e7f48

当person1添加了KVO之后,setAge:方法的调用位置发生变化,后面的调用位置是Foundation类的_NSSetIntValueAndNotify方法。

疑问:_NSSetIntValueAndNotify方法里面做了什么?

验证方法:增加监听方法和触发KVO的点击事件

objectivec 复制代码
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    if ([keyPath isEqualToString:@"age"]) {
        NSLog(@"%@.%@ is changed, info: %@, value:\n%@", object, keyPath, context, change);
    }
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    self.person1.age = 20;
}

当触发了KVO,输出内容如下:

ini 复制代码
-[GSPerson willChangeValueForKey:]
-[GSPerson setAge:]
didChangeValueForKey: -- begin
<GSPerson: 0x600003bc9690>.age is changed, info: Person.age changed, value:
{
kind = 1;
new = 20;
old = 0;
}
didChangeValueForKey: -- end

发现触发了3个方法:willChangeValueForKey:、setAge、didChangeValueForKey:,说明_NSSetIntValueAndNotify函数里面调用了这几个方法,并且,在didChangeValueForKey:方法里面调用了监听方法observeValueForKeyPath:ofObject:change:context:

二、手动触发KVO

  1. 只有调用了setAge:才会触发KVO,如何在不调用setAge:方法的前提下触发KVO呢?

答:调用willChangeValueForKey: 和 didChangeValueForKey:方法,传入对应的key即可。

csharp 复制代码
[self.person1 willChangeValueForKey:@"age"];
[self.person1 didChangeValueForKey:@"age"];
  1. 直接修改成员变量(_age)会触发KVO吗?

不会,直接修改成员变量并没有调用setAge:方法。

三、附加知识点

  1. 获取类对象的所有方法

遍历类对象的方法列表

ini 复制代码
#import <objc/runtime.h>

- (void)printMethodNamesOfClass:(Class)cls {
    NSMutableString* methodNames = [NSMutableString string];
    unsigned int count;
    Method *methodList = class_copyMethodList(cls, &count);
    for (NSUInteger i = 0; i < count; i ++) {
        Method method = methodList[i];
        NSString *methodName = NSStringFromSelector(method_getName(method));
        [methodNames appendFormat:@"%@, ",methodName];
    }
    NSLog(@"%@的所有方法:%@", cls, methodNames);
}

[self printMethodNamesOfClass:object_getClass(self.person1)];

输出:NSKVONotifying_GSPerson的所有方法:setAge:, class, dealloc, _isKVOA
  1. 为什么NSKVONotifying_GSPerson类要重写class方法?

直接返回GSPerson类,以屏蔽KVO的内部实现,隐藏NSKVONotifying_GSPerson类。

正因为这样,当self.person1添加了KVO,如果用[self.person1 class]方法获取类对象,发现输出的是GSPerson类对象,只有使用运行时方法object_getClass(self.person1)才能拿到正确的类NSKVONotifying_GSPerson。

相关推荐
2501_916007476 分钟前
iOS 26 软件性能测试 新版系统下评估全流程 + 多工具辅助方案
android·macos·ios·小程序·uni-app·cocoa·iphone
00后程序员张1 小时前
Swoole HTTPS 实战,在生产环境部署、性能权衡与排查流程
后端·ios·小程序·https·uni-app·iphone·swoole
2501_915909064 小时前
iOS App 上架全流程详解:证书配置、打包上传、审核技巧与跨平台上架工具 开心上架 实践
android·ios·小程序·https·uni-app·iphone·webview
2501_915106324 小时前
iOS 26 系统流畅度测试实战分享,多工具组合辅助策略
android·macos·ios·小程序·uni-app·cocoa·iphone
2501_915918414 小时前
开发 iOS 应用全流程指南,环境搭建、证书配置与跨平台使用 开心上架 上架AppStore
android·ios·小程序·https·uni-app·iphone·webview
方君宇5 小时前
iOS App小组件(Widget)显示LottieFiles动画和GIF图片
ios
Digitally5 小时前
如何将 iPhone 联系人同步到 Mac
macos·ios·iphone
Digitally5 小时前
无需 iCloud 在 iPhone 之间传输文本消息
ios·iphone·icloud
2501_9159214313 小时前
iOS 是开源的吗?苹果系统的封闭与开放边界全解析(含开发与开心上架(Appuploader)实战)
android·ios·小程序·uni-app·开源·iphone·webview
2501_9159090616 小时前
原生 iOS 开发全流程实战,Swift 技术栈、工程结构、自动化上传与上架发布指南
android·ios·小程序·uni-app·自动化·iphone·swift