iOS-KVO(二) 使用注意点
2019-07-03 本文已影响0人
厦门_小灰灰
iOS-KVO(一) 基本操作
iOS-KVO(二) 使用注意点
iOS-KVO(三) 窥探底层实现
iOS-KVO(四) 自定义KVO+Block
- KVO在使用时添加观察者和移除观察者应当成对出现;
- 被观察者在销毁前,要移除所有的观察者,iOS10以下会崩溃,iOS11以上不会崩溃;
重复添加观察者
创建两个类,KVOObjectA和KVOObjectB,其中KVOObjectB类创建实例对象是KVOObjectA类创建的实例对象的观察者。
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface KVOObjectA : NSObject
@property (copy, nonatomic) NSString *nameA;
@end
NS_ASSUME_NONNULL_END
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface KVOObjectB : NSObject
@property (copy, nonatomic) NSString *nameB;
@end
NS_ASSUME_NONNULL_END
#import "KVOObjectB.h"
@implementation KVOObjectB
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
NSLog(@"%@", change);
}
@end
我们模拟两次对objcA的nameA属性添加观察者objcB,当属性值发生变化时,接收方法会执行两次。
注:添加观察者几次,也要保证移除观察者几次
self.objcA = [KVOObjectA new];
self.objcB = [KVOObjectB new];
//两次添加,两次移除
[self.objcA addObserver:self.objcB forKeyPath:@"nameA" options:NSKeyValueObservingOptionNew context:NULL];
[self.objcA addObserver:self.objcB forKeyPath:@"nameA" options:NSKeyValueObservingOptionNew context:NULL];
self.objcA.nameA = @"hui";
[self.objcA removeObserver:self.objcB forKeyPath:@"nameA"];
[self.objcA removeObserver:self.objcB forKeyPath:@"nameA"];
self.objcB = nil;
self.objcA = nil;
打印结果:
2019-07-03 20:52:11.255950+0800 KVODemo[5867:145616] {
kind = 1;
new = hui;
}
2019-07-03 20:52:11.256140+0800 KVODemo[5867:145616] {
kind = 1;
new = hui;
}
如果少一次移除,被观察者销毁时还存在观察者,程序就会发生崩溃,崩溃原因:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'An instance 0x7fe61252ea70 of class KVOObjectA was deallocated while key value observers were still registered with it. Current observation info: <NSKeyValueObservationInfo 0x7fe612535720> (
<NSKeyValueObservance 0x7fe61251bd50: Observer: 0x7fe61252e740, Key path: nameA, Options: <New: YES, Old: NO, Prior: NO> Context: 0x0, Property: 0x7fe612527970>
)'
注:iOS10及其以下会崩溃,iOS11以上不会崩溃;
删除不存在的观察者
self.objcA = [KVOObjectA new];
self.objcB = [KVOObjectB new];
[self.objcA removeObserver:self.objcB forKeyPath:@"nameA"];
self.objcB = nil;
self.objcA = nil;
objA对象并没有添加objcB对象为观察者,直接去移除其观察者,会导致崩溃。
Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer <KVOObjectB 0x60000235b590> for the key path "nameA" from <KVOObjectA 0x60000235aee0> because it is not registered as an observer.'
KVO在使用时添加观察者和移除观察者应当成对出现
移除一个已经销毁的观察者
self.objcA = [KVOObjectA new];
self.objcB = [KVOObjectB new];
[self.objcA addObserver:self.objcB forKeyPath:@"nameA" options:NSKeyValueObservingOptionNew context:NULL];
self.objcB = nil;
[self.objcA removeObserver:self.objcB forKeyPath:@"nameA"];
self.objcA = nil;
执行后闪退了
Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer <(null) 0x0> for the key path "nameA" from <KVOObjectA 0x600002efe8e0> because it is not registered as an observer.'
总结
- 添加观察者和移除观察者要相对应;
- 不要将已经释放的观察者对象,再进行移除;
- 可以多次对同一个属性添加相同的观察者,当属性更改的时候,会多次调用接收方法,不过移除观察者也要执行多次;
- 在iOS10及其以下,不移除观察者会出现闪退的情况,在iOS11及其以上,不会出现闪退的情况;