12

In short, when the property value changing, I have to update some logic in my code, for example:

- (void)setProp:(NSString *)theProp
{
  if (prop != theProp){
    [prop release];
    prop = [theProp copy];
    [self myLogic];
  }
}

or:

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context
{
  if ([keyPath isEqualToString:@"prop"]){
    [self myLogic];
  }  
}

Which is the best way, and WHY?

EDIT: I prefect the second way, because I don't know what the compiler will generate @synthesize directive for me, I choose to believe the compiler is more smarter than my owe setter implementation, hence I will not break something.

cxa
  • 4,238
  • 26
  • 40

3 Answers3

5

The first snippet is the way to go, if you’re interested in changes in the same object. You only need to use the second if you are interested in changes to other objects, using it to observe self is overkill.

Sven
  • 22,475
  • 4
  • 52
  • 71
  • How is it overkill? Say you want to observe your own computed property which depends on 5 other ones. You don't really want to write 5 getters just for that do you? – Christian Schnorr Mar 23 '15 at 12:16
  • As always you can only say it depends. For simple cases as described in this question setting up KVO is overkill. For more complicated use cases like you describe KVO might be reasonable. – Sven Mar 23 '15 at 18:56
  • It may indeed be a little overhead, but I prefer keeping my code properly grouped rather than having observation logic distributed over 20 methods. Objective-C is not - at never was - about performance. I really think this overhead is worth it for clarity, consistency and the ability to scale super well. – Christian Schnorr Mar 23 '15 at 19:10
3

Tough call, IMHO both options suck.

The first one forces you to write your own setter, which is a lot of boilerplate code. (Not to mention that you have to remember to also fire KVO notifications with willChangeValueForKey: and didChangeValueForKey: if you want KVO for the property in question to work.)

The second option is also quite heavy-weight and your implementation is not enough. What if your superclass also has some KVO in place? You’d have to call super somewhere in the handler. If not, are you sure your superclass won’t change? (More about KVO in a related question.)

Sometimes you can sidestep the issue using other methods like bindings (if you’re on a Mac) or plain notifications (you can post a notification that a model changed and all interested parties should refresh).

If you have many recalculations like this and can’t find any better way around, I would try to write a superclass with better observing support, with interface like this:

[self addTriggerForKeyPath:@"foo" action:^{
    NSLog(@"Foo changed.");
}];

This will take more work, but it will keep your classes cleanly separated and you can solve all KVO-related problems on one place. If there are not enough recalculated properties to make this worthwile, I usually go with the first solution (custom setter).

Community
  • 1
  • 1
zoul
  • 102,279
  • 44
  • 260
  • 354
  • 7
    When you have a property and you are overwriting/implementing the setter, you [don't need to call `willChangeValyeForKey:` and `didChangeValueForKey:`](http://stackoverflow.com/questions/3261139/do-you-need-to-call-willchangevalueforkey-and-didchangevalueforkey), the compiler already does that for you. – DarkDust May 05 '11 at 07:00
  • 1
    Right, you usually don’t need to call `will/didChangeValueForKey:`. But it’s not the compiler that makes this happen - it’s the runtime. If you call this yourself in a simple setter those methods will be called twice, unless you also implement the class method `automaticallyNotifiesObserversForKey:` to return `NO` for the given property. – Sven May 05 '11 at 07:04
  • Ah! Yes, sorry, you’re right. I forgot that the notifications are not sent by the accessor in the class, but by the swizzled method. – zoul May 05 '11 at 07:21
0

In your case the best option is to add the logic inside the setter. Your setter implementation is correct if your property declaration is something like

@property (nonatomic, copy) YCYourClass *prop;

KVO is used normally when checking for changes on external objects properties.

NSNotifications are better suited to inform of events.

Ivan Sanchez
  • 539
  • 4
  • 8