当控制器被释放时,键值观察者仍然在其中注册

key value observers were still registered with it when controller is deallocated

我在代码中添加了一个观察者,然后在 dealloc 和 viewWillDisappear 中删除了它,但我仍然收到一条错误消息

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'An instance 0x167e5980 of class MyController2 was deallocated while key value observers were still registered with it.

Current observation info: <NSKeyValueObservationInfo 0x16719f90> (
<NSKeyValueObservance 0x16719fb0: Observer: 0x167e5980, Key path: dataContainer.report, Options: <New: YES, Old: YES, Prior: NO> Context: 0x0, Property: 0x1677df30>
)'

我创建了一个控制器,MyController 并从中派生了一个新控制器 MyController2。现在我在 MyController2.

中添加了 KVO
- (void)viewDidLoad {
    [super viewDidLoad];
    [self addObserver:self forKeyPath:@"dataContainer.report" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
}

然后在 observeValueForKeyPath 中:-

- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {

    id oldC = [change objectForKey:NSKeyValueChangeOldKey];
    id newC = [change objectForKey:NSKeyValueChangeNewKey];

    if([keyPath isEqualToString:@"dataContainer.report"]) {
        if (oldC != newC) {
            //Remove Observer

            [self removeObserver:self forKeyPath:@"dataContainer.report" context:nil];
            [self updateDataContainer];
            [self reportView];
        }
    }
}

然后我尝试在 viewWillDisappear 中删除观察者并 dealloc :-

- (void)dealloc {
    @try{
        [self removeObserver:self forKeyPath:@"dataContainer.report" context:nil];
    }@catch(id anException){
    }
}

-(void) viewWillDisappear:(BOOL)animated{
    @try{
        [self removeObserver:self forKeyPath:@"dataContainer.report" context:nil];
    }@catch(id anException){
    }
    [super viewWillDisappear:animated];
}

我查看了 lost of posts ,他们都说一件事你需要删除观察者。我试图从他们两个中删除观察者,但我仍然遇到问题。

你应该有一个布尔标志,你应该在添加观察者时将其设置为 true,在删除时将其设置为 false。仅当此标志为 false 时才添加观察者。 在移除观察者之前还要在 viewWillDisappear 中添加检查。同时添加日志

    if (self.isMovingFromParentViewController || self.isBeingDismissed) {
        if (isReportKVOAdded) {
            [self removeObserver:self forKeyPath:@"dataContainer.report" context:nil];
        }
    }

根据我的经验,在 iOS 中添加和删除观察者的最佳方式。

在ViewDidLoad中添加观察者:-

- (void)viewDidLoad {
    [super viewDidLoad];
    [self addObserver:self forKeyPath:@"dataContainer.report" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
}

要观察观察者,我们必须这样做:-

Don't remove observer in observeValueForKeyPath

- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {

    id oldC = [change objectForKey:NSKeyValueChangeOldKey];
    id newC = [change objectForKey:NSKeyValueChangeNewKey];

    if([keyPath isEqualToString:@"dataContainer.report"]) {
        if (oldC != newC) {
            [self updateDataContainer];
            [self reportView];
        }
    }
}

在 dealloc 中删除观察者:

call remove once here

- (void)dealloc {
    @try{
        [self removeObserver:self forKeyPath:@"dataContainer.report" context:nil];
    }@catch(id anException){
    }
}

Swift 4 KVO 使用 KeyPath 观察器在 iOS 11(可能还有 macOS 10.13)

上工作
    @objcMembers class Foo: NSObject {
        dynamic var string = "bar"
    }

    var observation: NSKeyValueObservation!

    func TestKVO() {

        var foo = Foo()

        // kvo in 2 lines of code
        observation = foo.observe(\.string) { observed, change in
            print(observed.string)
        }

        foo.string = "yo" // prints "yo"
        foo = Foo() // works on iOS 11, crashes MacOS 10.12, not tested on MacOS 10.13, yet
        foo.string = "oy" // does not print
    }

macOS 10.13 and iOS 11 Release Notes