OC-KVO的本质分析

KVO的全称是Key-Value Observing,俗称“键值监听”,可以用于监听某个对象属性值的改变。

1.KVO的使用

代码如下:

@interface Person : NSObject

@property (nonatomic, assign) int age;

@end

----使用----

@interface ViewController ()

@property (nonatomic, strong) Person *p1;
@property (nonatomic, strong) Person *p2;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.p1 = [[Person alloc] init];
    self.p1.age = 1;
    
    self.p2 = [[Person alloc] init];
    self.p2.age = 2;
    
    //给对象p1的age添加监听
    NSKeyValueObservingOptions option = NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew;
    [self.p1 addObserver:self forKeyPath:@"age" options:option context:nil];
}

//修改属性的值
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    self.p1.age = 10;
    self.p2.age = 20;
}

//监听的回调
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    NSLog(@"object = %@, keyPath = %@ change = %@", object, keyPath, change);
}

打印的结果如下:

可知,监听到了p1对象中age值的改变。

2.疑问

从上面的那段代码中,p1 和 p2都设置了age的值,相当于调用了setAge:这个方法,而setAge: 这个方法是实现是一样。那么为什么p1的setAge:这个方法执行完后会调用了监听的回调,而p2调用了setAge:的方法不会有监听的回调?

要解决这个疑问,我们需要去了解一下p1 和 p2的 类型,打个断点,查看p1 和 p2的isa指针类型如下:

打印的结果可以看出,p1的isa指针指向的不是Person的类对象,而是NSKVONotify_Person的类对象。也就是说,OC在运行时动态地创建了一个NSKVONotify_Person类,而这个类是继承于Person。

一般Person的对象,其内存布局如下:

但是添加了监听后的Person对象,其内存布局就变成了如下:

当p1调用setAge的方法时,不是直接调用Person类对象中的setAge:这个方法,而是先执行NSKVONotifying_Person类对象中的setAge:这个方法,这个方法调用的是Foundation中的_NSSetIntValueAndNotify这个方法。_NSSetIntValueAndNotify这个实现的逻辑大概如下:

(1)调用willChangeValueForKey: 通知开始用修改key的值了

(2)用super调用父类的setAge:的方法,即调用了Person的setAge:的方法

(3)调用didChangeValueForKey: 通知已经修改完key的值了。

3.验证

可以通过methodForSelector:这个方法来获取setAge:的地址,通过地址查看setAge:调用了哪个方法:

NSLog(@"%p %p", [self.p1 methodForSelector:@selector(setAge:)], [self.p2 methodForSelector:@selector(setAge:)] );

在命令行中用p (IMP)xxx地址,查看:

4.查看子类的方法

NSKVONotifying_Person除了重新了setAge:方法外,还有哪些方法呢?我们可以通过runtime的代码来获取:

- (void)getClassMethod:(Class)cls {
    unsigned int count = 0;
    Method *methodList = class_copyMethodList(cls, &count);
    NSMutableArray *methodArray = [NSMutableArray array];
    for (int i = 0; i < count; i++) {
        Method met = methodList[i];
        NSString *methodName = NSStringFromSelector(method_getName(met));
        [methodArray addObject:methodName];
    }
    NSLog(@"%@--%@", cls, methodArray);
    free(methodList);
}

-------调用如下:
[self getClassMethod:object_getClass(self.p1)];
[self getClassMethod:object_getClass(self.p2)];

查看结果如下:

runtime除了获取方法外,还提供了其他的接口获取其他的信息,如下图:

5.总结

注意:

如果直接修改成员变量是不会调用KVO的,因为kvo的本质是setter方法触发的。但是手动调用willChangeValueForKey和didChangeValueForKey这两个方法也会触发。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Flutter和Objective-C(简称OC)都是开发移动应用程序的技术,但它们在很多方面有着不同的特点。 首先,Flutter是一种跨平台的移动应用开发框架,由谷歌开发。它使用Dart语言编写,具有热重载、响应式UI框架和丰富的UI组件等特点。Flutter的一大优势是可以同时在iOS和Android平台上开发应用程序,并且拥有高性能和良好的用户体验。Flutter也支持使用原生代码进行集成,因此可以很好地与Objective-C进行交互。 Objective-C是一种面向对象的编程语言,主要用于iOS和macOS平台的应用程序开发。Objective-C采用了一种称为KVO(Key-Value Observing)的机制,允许对象对属性和值的变化进行观察和响应。通过注册观察者,当被观察对象的属性发生变化时,观察者可以接收到通知并执行相应的操作。KVO是一种非常强大的工具,可以用于实现对象之间的数据绑定和通信。 在使用Flutter开发应用时,可以与Objective-C进行集成,并利用Objective-C提供的KVO机制来实现对Flutter应用内部变量的监视和响应。这可以通过在Flutter与Objective-C之间建立桥接来实现,从而达到在Flutter应用中使用KVO的目的。 总的来说,Flutter和Objective-C KVO是两种不同的技术,Flutter是一个跨平台的移动应用开发框架,而Objective-C KVO是一种可以用于观察和响应对象属性变化的机制。在合适的场景下,可以通过Flutter与Objective-C进行集成,从而利用KVO机制来实现对Flutter应用内部变量的监视和响应。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值