Skip to content

Commit

Permalink
NSKVOSupport: Implement legacy KVO API
Browse files Browse the repository at this point in the history
Implements the setKeys:triggerChangeNotificationsForDependentKey: class
method. Please do not use it. It is fundamentally broken, and requires
the object's meta class to hold additional state.

Keys from this class method are the last resort when retrieving
dependencies via keyPathsForValuesAffectingValueForKey:.
This aligns with the implementation in Foundation.
  • Loading branch information
hmelder committed Feb 4, 2025
1 parent 732fd2d commit 8889e7e
Showing 1 changed file with 35 additions and 0 deletions.
35 changes: 35 additions & 0 deletions Source/NSKVOSupport.m
Original file line number Diff line number Diff line change
Expand Up @@ -569,9 +569,29 @@ - (bool) isEmpty

#pragma region KVO Core Implementation - NSObject category

static const char *const KVO_MAP = "_NSKVOMap";

@implementation
NSObject (NSKeyValueObserving)

+ (void) setKeys: (NSArray *) triggerKeys
triggerChangeNotificationsForDependentKey: (NSString *) dependentKey
{
NSMutableDictionary<NSString *, NSSet *> *affectingKeys;
NSSet *triggerKeySet;

affectingKeys = objc_getAssociatedObject(self, KVO_MAP);
if (nil == affectingKeys)
{
affectingKeys = [NSMutableDictionary dictionaryWithCapacity: 10];
objc_setAssociatedObject(self, KVO_MAP, affectingKeys,
OBJC_ASSOCIATION_RETAIN);
}

triggerKeySet = [NSSet setWithArray: triggerKeys];
[affectingKeys setValue: triggerKeySet forKey: dependentKey];
}

- (void) observeValueForKeyPath: (NSString *)keyPath
ofObject: (id)object
change: (NSDictionary<NSString *, id> *)change
Expand Down Expand Up @@ -631,6 +651,7 @@ + (NSSet *) keyPathsForValuesAffectingValueForKey: (NSString *)key
static NSSet *emptySet = nil;
static gs_mutex_t lock = GS_MUTEX_INIT_STATIC;
NSUInteger keyLength;
NSDictionary *affectingKeys;

if (nil == emptySet)
{
Expand Down Expand Up @@ -701,6 +722,20 @@ + (NSSet *) keyPathsForValuesAffectingValueForKey: (NSString *)key
{
return [self performSelector:sel];
}

// We compute an NSSet from information provided by previous invocations
// of the now-deprecated setKeys:triggerChangeNotificationsForDependentKey:
// if the original imp returns an empty set.
// This aligns with Apple's backwards compatibility.
affectingKeys = (NSDictionary *)objc_getAssociatedObject(self, KVO_MAP);
if (unlikely(nil != affectingKeys))
{
NSSet *set = [affectingKeys objectForKey:key];
if (set != nil)
{
return set;
}
}
}
return emptySet;
}
Expand Down

0 comments on commit 8889e7e

Please sign in to comment.