0

I have a class with a number of properties. When each property is set I need to update the UI of my application. The setters look something like this:

@synthesize trackHighlightColour = _trackHighlightColour;

- (void)setTrackHighlightColour:(UIColor *)trackHighlightColour
{
    _trackHighlightColour = trackHighlightColour;
    [self updateUI];
}

Rather than type this out 10 times, I would like to use a macro. This is what I have so far:

#if !defined(PROPERTY_SETTER)
#define PROPERTY_SETTER(PROPERTY_NAME, UPPER_PROPERTY_NAME) @synthesize (PROPERTY_NAME) = _(PROPERTY_NAME);\
\
- (void)set(UPPER_PROPERTY_NAME):(UIColor *)(PROPERTY_NAME)\
{\
    _(PROPERTY_NAME) = (PROPERTY_NAME);\
    [self updateUI];\
}
#endif

Unfortunately this has a few problems.

  1. (Minor) The need to repeat the property name with both casing
  2. The compiler does not allow me to pass the property name, instead I must pass it as a string:

    @implementation FooClass

    PROPERTY_SETTER(@"trackHighlightColour", "TrackHighlightColour");

    @end

It feels too close to give up on this technique. Does anyone have any suggestions?

ColinE
  • 68,894
  • 15
  • 164
  • 232
  • 1
    This looks like a good place to use Key Value Observation instead of a macro and direct UI updates in each setter. Have you considered that? The uppercase/lowercase problem is going to be a hard issue to get around in the macro. – gaige Mar 21 '13 at 08:22
  • @gaige From this question http://stackoverflow.com/questions/5893392/implement-own-setter-or-use-kvo it seems that KVO for observing 'self' is not seen as an elegant solution. – ColinE Mar 21 '13 at 08:35
  • 1
    That's certainly not my experience. Of course, the model shouldn't be updating the GUI anyway (the controller should be observing the model class and updating the GUI), but observing self works easily and passing KVO information to super is trivial. The only issue is inheriting from classes where you don't know if they're already doing KVO, and that shouldn't be an issue. – gaige Mar 21 '13 at 08:41

2 Answers2

3

Sounds like you want Key-Value Observing (KVO).

David M.
  • 3,667
  • 25
  • 27
  • Looks like KVO for observing properties of 'self' isn't a terribly elegant solution either http://stackoverflow.com/questions/5893392/implement-own-setter-or-use-kvo – ColinE Mar 21 '13 at 08:34
  • Using KVO directly is pain. I recommend using one of the block-based KVO extensions: [simple Block-KVO](https://github.com/iMartinKiss/Block-KVO) or [advanced ReactiveCocoa](https://github.com/ReactiveCocoa/ReactiveCocoa) – Tricertops Mar 21 '13 at 09:56
2

This is what I am using with MRC

#define GENERATE_RETAIN_SETTER(ATTRIBUTE_NAME, UPDATER) \
    if (_##ATTRIBUTE_NAME != ATTRIBUTE_NAME) { \
        [_##ATTRIBUTE_NAME release]; \
        _##ATTRIBUTE_NAME = [ATTRIBUTE_NAME retain]; \
        \
        [self UPDATER]; \
    }

Usage

- (void)setColor:(UIColor*)color {
    GENERATE_PROPERTY_SETTER(color, updateUI)
}

Result:

- (void)setColor:(UIColor*)color {
    if (_color != color) {
        [_color release];
        _color= [color retain];

        [self updateUI];
    }
}

It can be changed for your specific needs but there's no way how to capitalize the string in preprocessor. With the latest compiler, you can lose the @synthesize but to do exactly the thing you want, you can create a macro with four parameters

#define GENERATE_RETAIN_SETTER(PROPERTY, TYPE, SETTER, UPDATER) \
@synthesize PROPERTY = _##PROPERTY; \
\
- (void)SETTER:(TYPE*)PROPERTY { \
    if (_##PROPERTY != PROPERTY) { \
        [_##PROPERTY release]; \
        _##PROPERTY = [PROPERTY retain]; \
        \
        [self UPDATER]; \
    } \
}

Using

GENERATE_RETAIN_SETTER(color, UIColor, setColor, updateUI)

You should get

@synthesize color = _color;

- (void)setColor:(UIColor*)color {
    if (_color != color) {
        [_color release];
        _color= [color retain];

        [self updateUI];
    }
}
Sulthan
  • 128,090
  • 22
  • 218
  • 270
  • Still a few more lines of code than I would like. but this is still a pretty decent solution - thanks! – ColinE Mar 21 '13 at 09:22
  • @ColinE Just extended the answer a bit. – Sulthan Mar 21 '13 at 09:25
  • Have you tried out your updated version? popping `GENERATE_PROPERTY_SETTER(color, UIColor, setColor, updateUI)` into the body of a class results in a compilation error. – ColinE Mar 21 '13 at 09:51
  • @ColinE Just checked, it works for me but I had a typo with the macro name. – Sulthan Mar 21 '13 at 09:55