Reputation: 6590
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
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
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
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