概述
-
KVO(key value observing)允许对象监听另一个对象特定属性的改变,并在改变时接收到事件。一般继承自NSObject的对象都支持KVO -
KVO和NSNotificationCenter都是iOS中观察者模式的一种实现,区别如下。- 在于观察者和被观察者之间,KVO是一对一,NSNotificationCenter是一对多的关系。
KVO对被监听对象无侵入性,不需要修改其内部代码即可实现监听。
基本使用
分三步
-
注册观察者。
addObserver: forKeyPath: options: context:第一个参数observer:监听者 第二个参数keyPath:要监听的属性 第三个参数options: NSKeyValueObservingOptionNew 新值 NSKeyValueObservingOptionOld 旧值 NSKeyValueObservingOptionInitial 初始值 NSKeyValueObservingOptionPrior 在属性发生更改之前和之后收到两次通知,而不是改变之后才收到一次通知 第四个参数context:传入任意类型的对象,是kvo的一种传值方式调用此方法后,不会对观察者形成强引用。所以要注意观察者的释放时机,防止出现因观察者被释放导致crash
-
在观察者中实现回调方法。
observeValueForKeyPath: ofObject: change: context:有多种方式都会触发KVO。
// 点语法 self.car.carName = @"丰田FJ"; // setter方法 [self.car setCarName:@"丰田FJ"]; // setValue: forKey: [self.car setValue:@"丰田FJ" forKey:@"carName"]; // setValue: forKeyPath: [self setValue:@"丰田FJ" forKeyPath:@"car.carName"]; -
移除观察者。
removeObserver: forKeyPath: -
KVO原理
通过
isa-swizzling技术实现。 -
在运行时创建一个原类的子类,有固定格式
NSKVONotifying_XXX -
将对象的
isa指针指向新创建的类,重写NSKVONotifying_XXX的-class方法,返回原类的Class -
1.为了把流程看的明白一些,我们重写了一下类的- (NSString *)description方法 - (NSString *)description { NSLog(@"obj is %p \n",self); IMP carNameIMP = class_getMethodImplementation(object_getClass(self), @selector(setCarName:)); NSLog(@"IMP setCarName is %p \n",carNameIMP); Class objClass = [self class]; NSLog(@"obj class is %@ \n", objClass); Class objRuntimeClass = object_getClass(self); NSLog(@"obj runtime class is %@ \n", objRuntimeClass); NSLog(@"obj method list is: \n"); unsigned int count; Method *methodList = class_copyMethodList(objRuntimeClass, &count); for (NSInteger i = 0; i < count; i++) { Method method = methodList[i]; NSString *methodName = NSStringFromSelector(method_getName(method)); NSLog(@"method%ld = %@ \n", (long)i, methodName); } NSLog(@"\n"); return @""; }2. 在添加监听者前后分别调用`description`方法 [self.car description]; // 添加监听者 [self.car addObserver:self forKeyPath:@"carName" options:(NSKeyValueObservingOptionNew) context:@"我想传啥就传啥 nil也行"]; [self.car description]; 3. 打印结果如下 // 注册监听者之前调用的 description obj is 0x281368880 IMP setCarName is 0x1001cbe3c obj class is USTestCar obj runtime class is USTestCar obj method list is: method0 = setCarName: method1 = carName method2 = .cxx_destruct method3 = description // 注册监听者之后调用的 description obj is 0x281368880 IMP setCarName is 0x1f5a46340 // setter方法重写 obj class is USTestCar obj runtime class is NSKVONotifying_USTestCar // 生成的子类 obj method list is: method0 = setCarName: method1 = class method2 = dealloc method3 = _isKVOA // KVO自动生成的子类标识 -
子类重写
setter方法。在修改值之前调用willChangeValueForKey:;修改值之后调用didChangeValueForKey,这两个方法最终都会被调用到observeValueForKeyPath: ofObject: change: context:这个回调方法中可以自己调用`willChangeValueForKey`和`didChangeValueForKey`。即可在不改变属性值的情况下手动触发KVO。 手动触发KVO 这两个方法必须同时调用。 [self.car willChangeValueForKey:@"carName"]; [self.car didChangeValueForKey:@"carName"]; -
4.最后在- (void)dealloc中移除KVO
-
- (void)dealloc { [self.car removeObserver:self forKeyPath:@"carName"]; [self.car description]; }打印结果如下:
-
obj is 0x600003260c00 IMP setCarName is 0x10fb9bdb0 obj class is USTestCar obj runtime class is USTestCar obj method list is: method0 = .cxx_destruct method1 = description method2 = setCarName: method3 = carName衍生出来的NSKVONotifying_USTestCar已经“销毁”,setter方法也都恢复,指针指向了原类。整个KVO的流程结束。
本文深入探讨了KVO(Key-Value Observing)在iOS开发中的应用,详细讲解了KVO与NSNotification的区别,以及如何通过isa-swizzling技术实现属性监听。文章提供了KVO的基本使用步骤,包括注册、回调及移除观察者的方法,并解析了KVO原理,展示了监听前后对象的内部变化。

6061

被折叠的 条评论
为什么被折叠?



