Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

KVO and refactoring

Lets say that I have:

@property NSNumber* number;

And my controller is observing:

- (void)observeValueForKeyPath:(NSString *)keyPath ...
{
    if ([keyPath isEqualToString:@"number"]) ...
}

My question is - whats your approach to refactoring the name of number property?

It's obvious that I need to update observed key in observers code, but how could I do it in some smart/automatic way, and dont miss any observer watching to handle the change of my property?

like image 695
Maciek Czarnik Avatar asked Jan 16 '13 12:01

Maciek Czarnik


3 Answers

One way is to declare string constants for all the properties which are being observed. Use these constants for adding observer and comparing keypath. You should change the value of those string constants whenever you want to rename the property.

I dont think that complete atomisation is possible.

like image 122
Apurv Avatar answered Nov 04 '22 17:11

Apurv


Ok, I'll respond to myself:) My solution is a mix of Apurv solution and unit testing.

Here it is:

1 For each observed property in MyClass define:

static NSString* MyClassPropertyNameNumber = @"number";

2 In - (void)observeValueForKeyPath:(NSString *)keyPath ... implementation use only defined NSStrings.

- (void)observeValueForKeyPath:(NSString *)keyPath ...
{
    if ([keyPath isEqualToString:MyClassPropertyNameNumber]) ...
}

3 Write the unit test which will check if MyClass object responds to setNumber: and number selectors.

- (void)testMyClass
{
    SEL numberGetter = NSSelectorFromString(MyClassPropertyNameNumber);
    SEL numberSetter = NSSelectorFromString([NSString stringWithFormat:@"set%@:", MyClassPropertyNameNumber]);
    
    MyClass* testMyClass = [[MyClass alloc] init];
    if (![testMyClass respondsToSelector:numberGetter] || ![testMyClass respondsToSelector:numberSetter])
    {
        STFail(@"%@: %@ property name has changed! Please update your defined property name!", NSStringFromClass([MyClass class]), MyClassPropertyNameNumber);
    }
}

It will fail if you'll change property name, and dont update defined property name.

I hope it will be helpful for someone:)

like image 3
Maciek Czarnik Avatar answered Nov 04 '22 18:11

Maciek Czarnik


Define a variable for each observed key path, use them as contexts when registering and in the observer handler method:

static void * numberKVO = &numberKVO;
static void * letterKVO = &letterKVO;

...
[self addObserver:self 
       forKeyPath:@"kp.4.number" 
          options:NSKeyValueObservingOptionNew 
          context:numberKVO];
[self addObserver:self 
       forKeyPath:@"kp.4.letter" 
          options:NSKeyValueObservingOptionNew 
          context:letterKVO];   
...

- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if (context == numberKVO) {
        ...
    } else if (context == letterKVO) {
        ...
    }
}

You can modify the properties and key paths without changing anything else.

like image 1
djromero Avatar answered Nov 04 '22 17:11

djromero