iOS-KVO(二) 使用注意点

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

  1. KVO在使用时添加观察者和移除观察者应当成对出现;
  2. 被观察者在销毁前,要移除所有的观察者,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及其以上,不会出现闪退的情况;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 因为要结局swift3.0中引用snapKit的问题,看到一篇介绍Xcode8,swift3变化的文章,觉得很详细...
    uniapp阅读 4,542评论 0 12
  • KVO在OC中是实现键值(key-value-observing)观察的方式,在设计模式中是典型的观察者模式,当被...
    iOS猿_员阅读 359评论 0 0
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,148评论 1 32
  • 最近好长一段时间不动笔了,有些是因身体的原因,有些是因工作太过忙碌,读书的时间也是少之又少。期中考试临近,对学生学...
    心之山水阅读 586评论 1 0
  • 如果可以离开, 我想头也不回的走绝不再回来. 不管是坐车也好徒步也好, 我想翻越一座座山,穿过一片片树林. 这样我...
    觚不孤阅读 446评论 0 1