Chronor
Chronor

Reputation: 11

Odd problem using addObserver:forKeypath:options:context: in init method

According to Apple and numerous examples I've seen, there is no problem using KVO/KVC to observer yourself. Also according to those same sources, it's not a problem setting this up by using addObserver:forKeypath:options:context: in an object's init method, a la:

- (id)init
{
    self = [super init];
    if (self) {
    [self addObserver:self
               forKeyPath:@"selected"
                  options:NSKeyValueObservingOptionNew
                  context:NULL];
    }
    return self;
}

Unfortunately for some reason, my observer method does not get called when I do it there. If I move the addObserver call to another method and then invoke that method in the calling method:

MyObject *newObj = [[MyObject alloc] init];
[newObj setupObservers];

Then all is fine. This is a subclass of NSImageView, so it's not like there's any 'awakeFromNib'-type alternative here... I'm really scratching my head here and I'm sure I'm missing something obvious - like a rule about things which will cause KVO on self to not work in init methods, but I haven't found anything in the docs which would give me any hints here.

What do I not know?

Upvotes: 1

Views: 4804

Answers (5)

DennyLou
DennyLou

Reputation: 106

Basically you are trying to add a KVO notifications to an object that is not yet initialized (your init function add the observer before you return the self). Move the following code:

[self addObserver:self
       forKeyPath:@"selected"
          options:NSKeyValueObservingOptionNew
          context:NULL];

into the - (void)viewDidLoad instead. It's gonna be fine.

Upvotes: 0

Jonathan Dann
Jonathan Dann

Reputation: 280

As for the context pointer, the preferred way is:

static void *MyPrivateObservationContext = (void*)@"MyPrivateObservationContext"; // we assume MyPrivateObservationContext is a unique name, I use something of the form ClassNamePropertyObservationContext

then

-[obj add....... context:&MyPrivateObservationContext];

Then in

-(void)observeValueForKeyPath:....context:c;  
{  
    if (c == &MyPrivateObservationContext) {  
        // do work  
    } else {  
        [super observeValueForKeyPath:...];  
    }  
}

Upvotes: 2

user96459
user96459

Reputation: 456

The problem is probably that -init is not invoked in your case, -initWithCoder: is.

Every Cocoa class has a set of init methods called its "designated initializers". Each object, as its being instantiated, is guaranteed to go through one and only one of the designated initializers of each class in its inheritance tree.

If you're subclassing a class and have initialization to do, you must override all of the designated initializers of the superclass.

The designed initializers of NSImageView are -initWithCoder: and initWithFrame:. Override those two, not init.

Upvotes: 4

Rob Napier
Rob Napier

Reputation: 299455

This is a subclass of NSImageView, so it's not like there's any 'awakeFromNib'-type alternative here...

I don't understand this point. Are you creating this object in a NIB or not? If a NIB is creating this object, then it will call -awakeFromNib. The first thing you should establish (using NSLog()) is whether your -init is actually running. When nothing happens, it usually means code did not run.

Upvotes: 0

Aviad Ben Dov
Aviad Ben Dov

Reputation: 6409

I'm not sure whether there is such restriction or not, but even if you don't have a awakeFromNib you can create one by adding setupObservers to the run-loop in your init method:

[[NSRunLoop currentRunLoop] 
  performSelector:@selector(setupObservers) 
  target:self 
  argument:nil 
  order:1 
  modes:NSDefaultRunLoopMode];

Upvotes: 0

Related Questions