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
The problem is not @synchronized
. There are several issues with your code:
+[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
?
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]
).+[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
.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.