返回

iOS 底层探索:自定义 KVO(四)——完善自定义 KVO

IOS

自定义KVO(四):完善自定义 KVO

在上一篇博客中,我们已经初步实现了自定义 KVO,但还有一些问题需要解决。本篇博客将对自定义 KVO 进行完善。

解决循环引用

在自定义 KVO 的实现中,Observer 会持有被观察对象的引用,而被观察对象也会持有 Observer 的引用。这可能会导致循环引用,从而导致内存泄漏。为了解决这个问题,我们需要打破这种循环引用。

我们可以使用 WeakReference 来持有被观察对象的引用,这样当被观察对象被销毁时,Observer 不会持有它的强引用。

@implementation Observer

- (instancetype)initWithObject:(id)object keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options changeBlock:(void (^)(id oldValue, id newValue))changeBlock {
    if (self = [super init]) {
        _object = object;
        _keyPath = keyPath;
        _options = options;
        _changeBlock = changeBlock;
        _weakObject = object;
    }
    return self;
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    if (_weakObject == nil) {
        // 被观察对象已经被销毁,移除观察
        [object removeObserver:self forKeyPath:keyPath];
        return;
    }
    _changeBlock(change[NSKeyValueChangeOldKey], change[NSKeyValueChangeNewKey]);
}

@end

避免意外崩溃

在自定义 KVO 的实现中,如果在观察期间被观察对象被销毁,可能会导致崩溃。为了避免这种情况,我们需要在观察期间检查被观察对象是否仍然存在。

我们可以通过在观察方法中判断 _weakObject 是否为 nil 来检查被观察对象是否仍然存在。如果为 nil,则表示被观察对象已经被销毁,我们需要移除观察并返回。

支持多层嵌套观察

自定义 KVO 还支持多层嵌套观察。这意味着我们可以观察一个对象的属性,该属性本身也是一个对象,该对象也有可以观察的属性。

为了支持多层嵌套观察,我们需要在观察方法中递归调用 observeValueForKeyPath: 方法。这样,当被观察对象的属性发生变化时,我们可以继续观察嵌套对象的属性。

自定义 KVO 的高级用法

除了基本功能外,自定义 KVO 还可以进行一些高级用法,例如:

  • KVO 队列: 我们可以指定一个队列来执行 KVO 的回调。这样,我们可以控制 KVO 回调是在主线程还是其他线程执行。
  • KVO 上下文: 我们可以传递一个上下文对象给 KVO 回调。这样,我们可以将其他信息传递给 KVO 回调。
  • KVO 过滤: 我们可以使用 NSKeyValueObservingOptions 来过滤 KVO 回调。这样,我们可以只对特定的属性变化感兴趣。

结论

通过完善自定义 KVO 的实现,我们解决了循环引用、意外崩溃等问题,并支持了多层嵌套观察和高级用法。自定义 KVO 是一种强大的技术,可以帮助我们灵活地观察对象的属性变化,并做出相应的处理。