Resh32
Resh32

Reputation: 6590

KVO for whole object properties

Is it possible to add an observer to get notified if any of the monitored object properties is modified? For example:

@interface OtherObject : NSObject

@property (nonatomic) MyObject* myObject;

@end

and

@interface MyObject : NSObject

@property (nonatomic) unsigned int property1;
@property (nonatomic) unsigned int property2;

@end

I would like to do something like:

[otherObject addObserver:self
               forKeyPath:@"myObject"
                  options:0
                  context:nil]

and get notified if either property1 or property2 is modified. It does not seem to work if I register the holding object (somehow makes sense because myObject is not really modified when I modify property1 for example).

Upvotes: 11

Views: 2900

Answers (3)

Yonat
Yonat

Reputation: 4608

Swift version of DrummerB answer, with an additional function to remove the observations (eg in deinit):

extension NSObject {
    func addObserverForAllProperties(
        observer: NSObject,
        options: NSKeyValueObservingOptions = [],
        context: UnsafeMutableRawPointer? = nil
    ) {
        performForAllKeyPaths { keyPath in
            addObserver(observer, forKeyPath: keyPath, options: options, context: context)
        }
    }

    func removeObserverForAllProperties(
        observer: NSObject,
        context: UnsafeMutableRawPointer? = nil
    ) {
        performForAllKeyPaths { keyPath in
            removeObserver(observer, forKeyPath: keyPath, context: context)
        }
    }

    func performForAllKeyPaths(_ action: (String) -> Void) {
        var count: UInt32 = 0
        guard let properties = class_copyPropertyList(object_getClass(self), &count) else { return }
        defer { free(properties) }
        for i in 0 ..< Int(count) {
            let keyPath = String(cString: property_getName(properties[i]))
            action(keyPath)
        }
    }
}

Upvotes: 1

Fogmeister
Fogmeister

Reputation: 77661

What you could do is have a function to modify a particular property of the myObject...

-(void)setMyObjectName:(NSString*)name;

and then in the function have this code...

- (void)setMyObjectName:(NSString*)name
{
    [self willChangeValueForKey:@"myObject"];

    myObject.name = name;

    [self didChangeValueForKey:@"myObject"];
}

This will then notify the observer when that property on myObject is changed.

Whever you need this doing use this pattern and you can get notified for any change on myObject.

::EDIT:: Having said that, you should be able to use...

[otherObject addObserver:self
              forKeyPath:@"myObject.property1"
                 options:0
                 context:nil];

and that will observe property1 and do the same fort the other properties.

But this will mean adding an observer for each property individually.

Upvotes: 2

DrummerB
DrummerB

Reputation: 40221

I can think of two options.

  • You could create a separate "master" property and make it depend on all your other properties.

    @interface MyObject : NSObject
    @property (nonatomic) id masterProperty;
    @property (nonatomic) unsigned int property1;
    @property (nonatomic) unsigned int property2;
    @end
    

    + (NSSet *)keyPathsForValuesAffectingMasterProperty {
        return [NSSet setWithObjects:@"property1", @"property2", nil];
    }
    

    If you observe masterProperty you'll be notified when any of the properties change.

  • You use the Objective-C runtime to get a list of all the properties and observe them all.

    - (void)addObserverForAllProperties:(NSObject *)observer
                                options:(NSKeyValueObservingOptions)options
                                context:(void *)context {
        unsigned int count;
        objc_property_t *properties = class_copyPropertyList([self class], &count);
        for (size_t i = 0; i < count; ++i) {
            NSString *key = [NSString stringWithCString:property_getName(properties[i])];
            [self addObserver:observer forKeyPath:key 
                      options:options context:context];
        }
        free(properties);
    }
    

Upvotes: 15

Related Questions