1

I found some code that looks like:

if (statisticsObject.idag3_orig != 0) {
    statisticsView.idag3.text = [NSString stringWithFormat:@"%i",statisticsObject.idag3_orig];
} else {
    float compare1 = statisticsObject.idag2;
    float compare2 = statisticsObject.idag3;
    float result = compare1 + (compare1 * (compare2 / (float) 100.00));
    int final = (int)roundf(result);
    statisticsView.idag3.text = [NSString stringWithFormat:@"%i",final];
}

if (statisticsObject.igar3_orig != 0) {
    statisticsView.igar3.text = [NSString stringWithFormat:@"%i",statisticsObject.igar3_orig];
} else {
    float compare1 = statisticsObject.igar2;
    float compare2 = statisticsObject.igar3;
    float result = compare1 + (compare1 * (compare2 / (float) 100.00));
    int final = (int)roundf(result);
    statisticsView.igar3.text = [NSString stringWithFormat:@"%i",final];
}

This is repeated many, many times. Obviously it doesn't feel very DRY, and is a bit of a pain to work with. How can I loop this logic with variable property names? I think the approach I've taken isn't allowed by Objective-C. Here's what I tried:

NSArray * properties = [[NSArray alloc] initWithObjects:
                                              @"foo",
                                              @"bar",
                                              @"spam", 
                                              nil];
for (id prop in properties) {
    NSLog(@"%@",obj.prop);
}

-- note --

My original pseudo-code was rather confusing. Sorry about that.

To put it simply, how can I restructure my code above so that I'm not constantly repeating myself? The mathematical operations performed are always the same.

Jezen Thomas
  • 13,619
  • 6
  • 53
  • 91

3 Answers3

1

This is mostly an architecture problem. Why 'foo1', 'foo2' and 'foo3' are not grouped in an object? They are integers, why not using an object with three integer properties x, y and z? Then define a method updateText on such object and call:

NSArray * properties = [[NSArray alloc] initWithObjects:
                                              obj.foo,
                                              obj.bar,
                                              obj.spam, 
                                              nil];
for (MyObject* object in properties) {
     [object updateText];
} 

Of course, what you want is also possible, accessing Obj-C runtime. The simplest solution would be to use NSSelectorFromString, e.g.

SEL sel1 = NSSelectorFromString([NSString stringWithFormat:@"%s%i", @"foo", 1]);

and then use performSelector, also with a NSInvocation to get primitive types.

Sulthan
  • 128,090
  • 22
  • 218
  • 270
  • I think I'm only confusing myself and others now. I've updated my question with the actual code I'm working with, not pseudo-code. – Jezen Thomas Nov 19 '12 at 11:54
  • My answer is still valid, abstract all the `idag...` and `igar...` properties into an object and make a method on it. Having properties named `idag1`, `idag2` and `idag3` is definitely wrong. There should be only one property called `idag` with properties to access the floats `1` `2` and `3` - with better names that just numbers, of course. – Sulthan Nov 19 '12 at 12:09
  • Sorry, I've tried it so many ways, and I just can't get it to work. I'm using ARC, which is preventing implicit type conversion. This feels as though it's much more complicated than it needs to be. Why is it so hard to iterate over some properties? Why can't I just switch out the property name for a variable? Here's what I'm trying to make work now: http://pastie.org/5401172 – Jezen Thomas Nov 19 '12 at 14:02
  • It's hard because it's something you shouldn't be doing! Please, read again the first part of my answer. The simplest solution is to redesign your architecture - you are using an object oriented language, so why are you not creating objects? Properties ending at numbers are always terrible. – Sulthan Nov 19 '12 at 14:22
  • Because it's outside the scope of the project. I agree with you. It's written poorly. My choices are to try and eliminate some repeated code, or effectively write the program from the ground up. The latter is just not viable. I don't understand why it's so hard to use a variable in place of a property like you can with other languages. – Jezen Thomas Nov 19 '12 at 14:26
  • What you want is reflexion. Only modern languages (since Java or so) have it natively. In all older languages, including Obj-C it is not very easy to use it. We have selectors which provide some reflexion over methods (including property getters) but access to variables is something very different. You have to dig very deep into obj-c runtime and it's C API. See http://stackoverflow.com/questions/2299841/objective-c-introspection-reflection – Sulthan Nov 19 '12 at 14:56
  • According to [this Wikipedia entry](http://en.wikipedia.org/wiki/Reflection_(computer_programming)), Obj-C has reflection. Or have I misunderstood? – Jezen Thomas Nov 19 '12 at 15:07
  • @JezenThomas It has reflexion but the high level API for it doesn't exist, with the exception of getting class by its name and selectors. You have to use runtime functions for everything else. – Sulthan Nov 19 '12 at 15:32
0

I don;t understand your second code, this is what it would work if you want to print the strings:

NSArray * properties = [[NSArray alloc] initWithObjects:
                                          @"foo",
                                          @"bar",
                                          @"spam", 
                                          nil];
for (id obj in properties) {
    NSLog(@"%@",obj);
}
Antonio MG
  • 20,382
  • 3
  • 43
  • 62
  • Ah, sorry, it was a little confusing. If the object is `obj`, I would like to iterate over its properties, effectively doing `NSLog(@"%@",obj.foo);`, then `NSLog(@"%@",obj.bar);` etc. – Jezen Thomas Nov 19 '12 at 11:33
0

With some care this code should work:

    NSArray * properties = [[NSArray alloc] initWithObjects:
                        @"foo",
                        @"bar",
                        @"spam",
                        nil];
for (id obj in properties)
{
    SEL selector = NSSelectorFromString(obj);
    if (selector && [statisticsView respondsToSelector:selector])
        NSLog(@"%@",[statisticsView performSelector:selector]);
}

please note that the NSLog may break if your properties don't return an NSObject