DevilInDisguise
DevilInDisguise

Reputation: 360

AddObserver forkeypath:: not responding

I have the following code which I'm using to try and observe a variable, and regenerate text when it changes. But so far nothing happens:/

-(void)viewdidload{    
    float Index = 1;//declared in header locally
    [self addObserver:self forKeyPath:@"Index" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

    [self generateAdviceText];
 }   



-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
NSLog(@"inside");
if([keyPath isEqualToString:@"Index"]){
    NSLog(@"INDEX CHANGED");
    [self generateAdviceText];
}
}

-(void)generateAdviceText{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSString *name = ((User *)appDelegate.users[self.currentUserIndex]).name;
NSString *clothWear = @"HAT";

NSMutableAttributedString *adviceString = [[NSMutableAttributedString alloc]initWithString:@"\"..."];
[adviceString appendAttributedString:[self boldString:name]];
[adviceString appendAttributedString:[self normalString:@", remember be extra aware of the cats today. The index is "]];
[adviceString appendAttributedString:[self boldString:[@(Index) stringValue]]];
[adviceString appendAttributedString:[self normalString:@", sth "]];
[adviceString appendAttributedString:[self boldString:clothWear]];
[adviceString appendAttributedString:[self normalString:@" sth "]];
[adviceString appendAttributedString:[self boldString:sth]];
[adviceString appendAttributedString:[self normalString:@"...\""]];

self.adviceLabel.attributedText = adviceString;

}

Can anybody spot my mistake? Thanks

Upvotes: 0

Views: 1057

Answers (2)

Ken Thomases
Ken Thomases

Reputation: 90611

Key-Value Observing does not generate change notifications for simple assignments to instance variables (or any other kind of variable).

In most cases, you need to call the setter of the property to generate a change notification for that object and that property.

At a low level, KVO only generates change notifications if the -willChange... and -didChange... methods from the NSKeyValueObserving informal protocol are called. Those methods are called in specific circumstances:

  • If a properly-named setter or collection mutation accessor for the property named by the key is called (assuming +automaticallyNotifiesObserversForKey: return true for that key). This is the best way.

  • If one of the -set... methods from the NSKeyValueCoding informal protocol, such as -setValue:forKey:, is called (again, assuming +automaticallyNotifiesObserversForKey: return true for that key).

  • If a collection proxy returned by one of the -mutable...ValueForKey: methods of the NSKeyValueCoding is sent mutation methods (again, depends on +automaticallyNotifiesObserversForKey:).

  • If some code (usually in the class defining the property) manually calls the -willChange... and -didChange... methods.

As Joe Shang noted, though, it's questionable for an object to observe itself. If an object wants to know when one of its properties has been changed, it should put the relevant code into its setter. Of course, it then has to modify its property exclusively using that setter, never by setting the instance variable directly (just like for KVO).

In the code you posted, though, it's just as well that you didn't get the KVO change notifications you expected. Your code would infinitely recurse until it crashed when it overflowed the stack. You attempted to make it so that changing the index calls -generateAdviceText and that -generateAdviceText changes the index.

Upvotes: 2

Joe Shang
Joe Shang

Reputation: 139

What's the Index type? static variable or property? The keyPath in addObserver:forKeyPath:options:contexts: must be the property of some object (KVC compatible), you can read NSHipster's acticle and objc.io #7 to get more.

In the other hand, if Index is property, you don't need to use KVO here. You can override the setter method of Index and do some action when Index is update.

- (void)setIndex:(int)index
{
    _index = index;
    // add you generateAdviceText method here
}

Upvotes: 1

Related Questions