user4992124 user4992124 -4 years ago 118
Objective-C Question

How to add KVO to synchronized class?

In my app I have the

Restaurant
class that you can see below. I'd like to attach a
KVOController
to it. But I'm having no luck. When I attach it with the code below, it crashes.

FBKVOController *KVOController = [FBKVOController controllerWithObserver:self];
self.KVOController = KVOController;

[self.KVOController observe:self keyPath:@"[Restaurant current].name.asString" options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew block:^(id observer, id object, NSDictionary *change) {
DDLogDebug(@"Restaurant changed");
}];


What's the best way to add KVO to a class like this?

@implementation Restaurant

static Restaurant *current = nil;

+ (Restaurant *)current {
@synchronized(self) {
if (current == nil) {
current = [[Restaurant alloc] initWithId:0];
}
}
return current;
}

- (id)initWithId:(NSInteger)number {
self = [super init];
if (self)
{
...
}
return self;
}

@end

Answer Source

The problem is not @synchronized. There are several issues with your code:

  • Do you want to observe when the current restaurant changes? Or when the current restaurant's name changes (without +[Restaurant current] pointing to a different restaurant instance). Or any kind of name change, whether triggered by a change of current or a change of name?
    • Depending on the answer, you'll either want to observe observe:[Restaurant class] or observe:[Restaurant instance], but definitely not observe:self (unless you're setting this up inside the Restaurant class implementation, in which case [self class] would be an alternative to [Restaurant class]).
    • For any change to be observable, you must ensure that the class is implemented in a KVO-compliant way. This goes both for changes to +[Restaurant current] as well as for changes to -[Restaurant name], depending on what you want to be able to observe.
  • [Restaurant current].name.asString is not a valid key path. Valid key paths may only contain property names (ASCII, begin with a lowercase letter, no whitespace) and dots to separate them (see Key-value coding for details). Once you're telling the KVOController to observe:[Restaurant class], all that remains for the key path is current.name.asString.
  • What is name if not a string? Do you really need to convert it to a string for observing it? If your intention is to watch for name changes, observing current.name is probably sufficient.

You'll likely end up with one of the following two options:

FBKVOController *kvoController = [FBKVOController controllerWithObserver:self];
[kvoController observe:[Restaurant class] keyPath:@"current.name" ...];`
// or
[kvoController observe:[Restaurant current] keyPath:@"name" ...];`

And again, for any changes to be observable, they need to be KVO-compliant.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download