Can I override setValue: forKey:?

key-value-coding, objective-c

Solution

There are definitely ways to do this. You could declare the relevant properties `@dynamic` and then use `+resolveInstanceMethod:` to actually create the setter on-demand. You can find an example like this in the sample code for iOS:PTL. This example demonstrates how to automatically make your accessors read and write to a dictionary (`properties`) rather than from ivars. To make this work for you, you'd probably need to always store `NSNull` in the dictionary and override the getter (`propertyIMP()`) to convert it to `nil`. So you'd change this:

[[self properties] setValue:value forKey:key];

to:

[[self properties] setValue:(value ?: [NSNull null]) forKey:key];

And change:

return [[self properties] valueForKey:NSStringFromSelector(_cmd)];

to:

id value = [[self properties] valueForKey:NSStringFromSelector(_cmd)];
return (value == [NSNull null] ? nil : value);

Or something like that.

BUT... unless this is a major win, I would avoid this kind of magic. My typical solution to this is to go the other way, and put it on the caller not to pass `[NSNull null]` when they shouldn't. I use a function like `RNNonNull()`:

id RNNonNull(id x) { return (x == [NSNull null]) ? nil : x; }

Then the caller is responsible for adding the wrapper:

obj.foo = RNNonNull(mystuff);

If that's not possible, I'd probably override the getters by hand to convert `NSNull` to `nil` rather than doing it with the runtime (it'd be a one-line method). The only reason I'd use the runtime is if there were a bunch of properties, and the object were very simple (purely a data object).

Problem

I have an object that is likely to have NSNull set as a value on all of its properties, and I'm working with code that doesn't play nice with NSNull values. Is it possible to override setValue: forKey: in my object like this? ``` -(void)setValue:(id)value forKey:(NSString *)key { if ([value isEqual:[NSNull null]]) { [super setValue:nil forKey:key]; } else { [super setValue:value forKey:key]; } } ``` The method doesn't get called when `object.property = [NSNull null]` gets set on it. How can I make this the default behavior for my setters?

Original source