iOS基础知识工作生活iOS

iOS-KVO(二) 使用注意点

2019-07-03  本文已影响0人  厦门_小灰灰

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.'

总结

上一篇下一篇

猜你喜欢

热点阅读