Maciek Czarnik
Maciek Czarnik

Reputation: 6181

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?

Upvotes: 5

Views: 290

Answers (4)

Maciek Czarnik
Maciek Czarnik

Reputation: 6181

Mogenerator is a great tool when it comes to deal with Core Data model classes. It generates NSStrings for all attributes, relationships, and fetched properties in:

extern const struct ModelClassAttributes
extern const struct ModelClassRelationships
extern const struct ModelClassFetchedProperties

Upvotes: 0

djromero
djromero

Reputation: 19641

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.

Upvotes: 1

Maciek Czarnik
Maciek Czarnik

Reputation: 6181

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:)

Upvotes: 3

Apurv
Apurv

Reputation: 17186

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.

Upvotes: 3

Related Questions