前言
我们知道, OC 中有三种对象:实例对象,类对象,元类对象。他们的关系如图:

我们复习一下这张图:
isa 属性用来沟通这三种对象
实例对象的 isa 指向(当前类的)类对象,类对象的 isa 指向(当前类的)元类对象。
superclass 属性用来找到这三种对象的父类
比如 Student 继承自 Person,Person 继承自 NSObject,那么在查找Student 内的属性,或者调用 Student 的方法时,就要用到 superclass:
如果 Student 没有 eat 方法,那么就通过 superclass 找到 Person,看看有没有 eat 方法,有的话直接调用。
复习
我们先复习一下 KVO 和 KVC。
KVO 全称 key value observing,翻译成“键值观察”,是 Foundation 框架中的重要组成部分,借助了 Runtime 的特性,通过 C 语言实现。
主要是如下两个方法:
@interface NSObject(NSKeyValueObserverRegistration)
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)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));
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;
@end
假设我们有 KSPerson 类:
@interface KSPerson : NSObject
@property (assign, nonatomic) int age;
@property (assign, nonatomic) int height;
@end
以及测试代码:
#import "ViewController.h"
#import "KSPerson.h"
#import <objc/runtime.h>
@interface ViewController ()
@property (strong, nonatomic) KSPerson *person1;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.person1 = [[KSPerson alloc] init];
self.person1.age = 1;
self.person1.height = 11;
NSLog(@"绑定前:%@",object_getClass(self.person1));
// 给person1对象添加KVO监听
NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
[self.person1 addObserver:self forKeyPath:@"age" options:options context:@"123"];
[self.person1 addObserver:self forKeyPath:@"height" options:options context:@"456"];
Class kvoClass = object_getClass(self.person1);
Class sprClass = class_getSuperclass(kvoClass);
NSLog(@"绑定后的类变成了:%@,父类:%@",kvoClass,sprClass);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
self.person1.age = 20;
self.person1.height = 30;
}
- (void)dealloc {
[self.person1 removeObserver:self forKeyPath:@"age"];
[self.person1 removeObserver:self forKeyPath:@"height"];
}
// 当监听对象的属性值发生改变时,就会调用
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
NSLog(@"监听到%@的%@属性值改变了 - %@ - %@", object, keyPath, change, context);
}
@end
那么点击屏幕的时候应该会触发,并且打印信息:
绑定前:KSPerson
绑定后的类变成了:NSKVONotifying_KSPerson,父类:KSPerson
这证实了:KVO 动态生成了新的类 NSKVONotifying_KSPerson。 接下来,我们深入剖析一下。
动态生成子类
先告诉大家结果,KVO 内部实现是这样的:
// 1. 重写 setter 方法,在设置值前后触发通知
- (void)setAge:(int)age {
[self willChangeValueForKey:@"age"];
[super setAge:age]; // 调用原始实现
[self didChangeValueForKey:@"age"];
}
// 2. 重写 class 方法,隐藏动态子类的存在
- (Class)class {
return class_getSuperclass(object_getClass(self)); // 返回父类(KSPerson)
}
// 3. 重写 dealloc 方法,清理资源我们最关注的部分无疑是动态生成子类是如何做到的。仍然直接给出答案:
改变对象的 isa指针:
// 添加观察者时
object_setClass(originalObject, generatedSubclass);
// 移除观察者时
object_setClass(observedObject, originalClass);
果博东方客服开户联系方式【182-8836-2750—】?薇- cxs20250806】
果博东方公司客服电话联系方式【182-8836-2750—】?薇- cxs20250806】
果博东方开户流程【182-8836-2750—】?薇- cxs20250806】
果博东方客服怎么联系【182-8836-2750—】?薇- cxs20250806】