iOS-KVO(二) 使用注意点

iOS-KVO(一) 基本操作
iOS-KVO(二) 使用注意点
iOS-KVO(三) 窥探底层实现
iOS-KVO(四) 自定义KVO+Block

  1. KVO在使用时添加观察者和移除观察者应当成对出现;
  2. 被观察者在销毁前,要移除所有的观察者,iOS10以下会崩溃,iOS11以上不会崩溃;

重复添加观察者

创建两个类,KVOObjectA和KVOObjectB,其中KVOObjectB类创建实例对象是KVOObjectA类创建的实例对象的观察者。

#import 

NS_ASSUME_NONNULL_BEGIN

@interface KVOObjectA : NSObject

@property (copy, nonatomic) NSString *nameA;

@end

NS_ASSUME_NONNULL_END
#import 

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 *)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:  (
 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  for the key path "nameA" from  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  because it is not registered as an observer.'

总结

  • 添加观察者和移除观察者要相对应;
  • 不要将已经释放的观察者对象,再进行移除;
  • 可以多次对同一个属性添加相同的观察者,当属性更改的时候,会多次调用接收方法,不过移除观察者也要执行多次;
  • 在iOS10及其以下,不移除观察者会出现闪退的情况,在iOS11及其以上,不会出现闪退的情况;

你可能感兴趣的:(iOS-KVO(二) 使用注意点)