From 9d31bd00261cc79d2e9ce212140ae8468aa1aee1 Mon Sep 17 00:00:00 2001 From: Frederik Seiffert Date: Thu, 19 Aug 2021 14:34:34 +0200 Subject: [PATCH 1/2] KVO: fix possible duplicate observing of key Could happen when using dependent key paths, as the dependency handling can have added an observer for restOfKeypath already. --- Frameworks/Foundation/NSKVOSupport.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Frameworks/Foundation/NSKVOSupport.mm b/Frameworks/Foundation/NSKVOSupport.mm index f33c38adcf..a42da633da 100644 --- a/Frameworks/Foundation/NSKVOSupport.mm +++ b/Frameworks/Foundation/NSKVOSupport.mm @@ -301,7 +301,7 @@ static void _addNestedObserversAndOptionallyDependents(_NSKVOKeyObserver* keyObs } // If restOfKeypath is non-nil, we have to chain on further observers. - if (keyObserver.restOfKeypath) { + if (keyObserver.restOfKeypath && !keyObserver.restOfKeypathObserver) { keyObserver.restOfKeypathObserver = _addKeypathObserver([object valueForKey:key], keyObserver.restOfKeypath, keypathObserver, keyObserver.affectedObservers); } From 207c750db33796d8dfdf3998e9e0645ea230837e Mon Sep 17 00:00:00 2001 From: Frederik Seiffert Date: Thu, 23 Sep 2021 17:48:04 +0200 Subject: [PATCH 2/2] KVO: fix case where nested observers were not being removed _dispatchWillChange() was not removing nested observers and optionally dependents if keypaths were in the process of changing. Now mirrors _dispatchDidChange() implementation. --- Frameworks/Foundation/NSKVOSupport.mm | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Frameworks/Foundation/NSKVOSupport.mm b/Frameworks/Foundation/NSKVOSupport.mm index a42da633da..666737cee9 100644 --- a/Frameworks/Foundation/NSKVOSupport.mm +++ b/Frameworks/Foundation/NSKVOSupport.mm @@ -552,9 +552,8 @@ static inline id _valueForPendingChangeAtIndexes( inline static void _dispatchWillChange(id notifyingObject, NSString* key, TFunc&& func) { _NSKVOObservationInfo* observationInfo = (__bridge _NSKVOObservationInfo*)[notifyingObject observationInfo]; for (_NSKVOKeyObserver* keyObserver in [observationInfo observersForKey:key]) { - _NSKVOKeypathObserver* keypathObserver = keyObserver.keypathObserver; - // Skip any keypaths that are in the process of changing. + _NSKVOKeypathObserver* keypathObserver = keyObserver.keypathObserver; if ([keypathObserver pushWillChange]) { // Call into the lambda function, which will do the actual set-up for pendingChanges func(keyObserver); @@ -569,10 +568,10 @@ inline static void _dispatchWillChange(id notifyingObject, NSString* key, TFunc& context:keypathObserver.context]; [change removeObjectForKey:NSKeyValueChangeNotificationIsPriorKey]; } - - // This must happen regardless of whether we are currently notifying. - _removeNestedObserversAndOptionallyDependents(keyObserver, false); } + + // This must happen regardless of whether we are currently notifying. + _removeNestedObserversAndOptionallyDependents(keyObserver, false); } }