2

I create an array of objects. These objects have a public variable correct. When I have xcode optimization turned off everything in my project works great, but when I have any sort of optimization set then things go awry. The very first time I create one of these objects the correct variable always returns false when accessed from another class. But subsequent creation of this class object returns the correct value for correct. Here is a snippit of my code and the problem.

.h

@interface ShellQuestionAnswer : UIView
{
    ...

    @public
    bool correct;
    ...
}

.m

-(void)setLetter:(NSString*)letter forAnswer:(XMLNode*)answer withResponse:(XMLNode*)response
{
    ...
    correct = [[[answer attributes] valueForKey:@"Correct"] isEqualToString:@"true"];
    //Putting NSLog here shows "correct" to be true
}

And the class that uses this object:

-(void)addQuestion:(XMLNode*) rootNode ID:(NSString*)ID
{
...
     answer1 = [[ShellQuestionAnswer alloc] initWithFrame:CGRectMake(0, currentY, textContainer.frame.size.width, 50)]; 
        [answer1 setLetter:@"A" forAnswer:[answerstextArray objectAtIndex:0] withResponse:[responsestextArray objectAtIndex:0]];
        currentY += answer1.frame.size.height + QUESTION_SEPARATION;
        if(!answerObjects)
        {
            answerObjects = [[NSMutableArray alloc] init];
        }
        else
        {
            [answerObjects removeAllObjects];
        }
        [answerObjects addObject:answer1];

        answer2 = [[ShellQuestionAnswer alloc] initWithFrame:CGRectMake(0, currentY, textContainer.frame.size.width, 50)];
        [answer2 setLetter:@"B" forAnswer:[answerstextArray objectAtIndex:1] withResponse:[responsestextArray objectAtIndex:1]];
        currentY += answer2.frame.size.height + QUESTION_SEPARATION;
        [answerObjects addObject:answer2];

        [textContainer addSubview:answer1];
        [answer1 setUserInteractionEnabled:YES];
        [textContainer addSubview:answer2];
        [answer2 setUserInteractionEnabled:YES];
        if([answerstextArray count] > 2)
        {
            answer3 = [[ShellQuestionAnswer alloc] initWithFrame:CGRectMake(0, currentY, textContainer.frame.size.width,50)];
            [answer3 setLetter:@"C" forAnswer:[answerstextArray objectAtIndex:2] withResponse:[responsestextArray objectAtIndex:2]];
            [textContainer addSubview:answer3];
            currentY += answer3.frame.size.height + QUESTION_SEPARATION;
            [answerObjects addObject:answer3];
            [answer3 setUserInteractionEnabled:YES];
        }
        if([answerstextArray count] > 3)
        {
            answer4 = [[ShellQuestionAnswer alloc] initWithFrame:CGRectMake(0, currentY, textContainer.frame.size.width, 50)];
            [answer4 setLetter:@"D" forAnswer:[answerstextArray objectAtIndex:3] withResponse:[responsestextArray objectAtIndex:3]];
            [textContainer addSubview:answer4];
            currentY += answer4.frame.size.height + QUESTION_SEPARATION;
            [answerObjects addObject:answer4];
            [answer4 setUserInteractionEnabled:YES];
        }

        [textContainer setNeedsDisplay]; 
        for(ShellQuestionAnswer *answer in answerObjects)
        {
            if(answer->correct)  //<-- always returns false the first time I run this function
            {
                if(!self.correctAnswer)
                {
                    self.correctAnswer = [[NSMutableArray alloc] init];
                }
                [self.correctAnswer addObject:answer];
            }
        }
}

I have logged everything and don't understand why the answer->correct returns false when outside the object, but inside the answer object the variable correct is actually true. And this only happens when optimization is on (any mode) and only on the first instance of this class being used.

If I change the correct variable into a property instead of a public class member variable then the issue is no longer present. I'm fine with this solution, but I am not fine with not understanding why this happens. This issue could be present in any of the other classes in my code and I do not know where until a customer complains of a random quirk the first time it runs. That is unacceptable to me. Anyone have any ideas on why optimization messes up my code?

EDIT: To be clearer, here are the test cases I am using. One works, one does not (in the fashion mentioned above).

Working:

@interface ShellQuestionAnswer : UIView
{
    ...

    @public
    //bool correct;
    ...
}
@property (nonatomic, assign) bool correct;

Not working:

@interface ShellQuestionAnswer : UIView
{
    ...

    @public
    bool correct;
    ...
}

Those are the only lines changed to get a working set (apart from answer->correct changing to answer.correct inside the for loop for the property case).

Putz1103
  • 6,211
  • 1
  • 18
  • 25
  • There is an interesting answer [here](http://stackoverflow.com/a/2182482/653513). In short: use accessor methods (ie: define properties) to access this values from outside the class. And you'll be safe. – Rok Jarc Apr 23 '13 at 16:41
  • A more complete code sample that reproduces the issue may help others help you. Also, are you using ARC? – Jon Shier Apr 23 '13 at 16:42
  • @ jshier Yes I am using ARC. Xcode 4.6.1. Complete code sample would be in the thousands of classes. That would be less than helpful. @rokjarc That answer just says what I am doing is allowed, it doesn't say why it doesn't work right when optimized. And I would rather avoid properties if possible. – Putz1103 Apr 23 '13 at 16:43
  • It also states "it is highly discouraged". Typically you would see this kind of coding in a .mm (c++ style) file rather than .m (objc). It is an interesting topic though. +1 for that. – Rok Jarc Apr 23 '13 at 16:48
  • @rokjark Understood. Most of my code is c++/objective-c++. What I'm asking about is straight objective-c. Thank you for your response. I understand discouraged, but if it's allowed (and provisioned for) it should work. – Putz1103 Apr 23 '13 at 17:00
  • 6
    I'm strongly suspicious that you have an error elsewhere in your code. There's an awful lot of stuff you haven't shown us, and a lot that's been "paraphrased". (Has nothing to do with using direct access vs properties, assuming there's no weird multithreading going on.) – Hot Licks Apr 23 '13 at 17:02
  • The 'answer->correct' form is generally discouraged. It's possible with OCJC++ that the generated code is incorrect. Try answer.correct and see if it makes any difference. Or even [answer correct] as another test. – ahwulf Apr 23 '13 at 17:19
  • @Hot Licks I'm not doing multithreading here, and yes there is a lot skipped out. I can't find anything that I bleeped that could cause a `->` to return something other than what it points to. But I very well could be missing something – Putz1103 Apr 23 '13 at 18:06
  • @ahwulf I mentioned in the post that the property works fine, the public member variable fails. So yes, `answer.correct` works fine. I'm solely worried as to why `answer->correct` does not. – Putz1103 Apr 23 '13 at 18:07
  • My first guess would be that somewhere your `answer` pointer is getting clobbered. – Hot Licks Apr 23 '13 at 18:31
  • If the member doesn't back the property, then the default underscore auto-generated iVar will be used. You have a case of terrible variable naming conflicts. – CodaFi Apr 23 '13 at 18:31
  • @CodaFi I use one or the other, never both at the same time (meaning if the class has the property in the .h then the variable is not present in the .h). I understand the naming conflicts with properties and variables and avoid them properly. – Putz1103 Apr 23 '13 at 18:33
  • @HotLicks That could be, but it only happens the very first instance I create of the class. And only when optimized code is set. All other instances of this class I create work properly with the `correct` variable being readable. – Putz1103 Apr 23 '13 at 18:34
  • I still want to know exactly how you're testing if it works "internally" but not "externally." Clearly your tests are influencing the result. Set a watch point on it, and see what's setting it. (I have a sinking feeling it's being assigned to a garbage value, but, as HotLicks says, you aren't showing nearly enough of a working example to get to the bottom of this) – CodaFi Apr 23 '13 at 18:39
  • @CodaFi I added the test case information, hopefully it explains exactly what I change to make it work vs. not. – Putz1103 Apr 23 '13 at 18:56
  • 1
    I am guessing you have some very good reason for direct instance variable access in objective-c rather than using a property(If you don't, use a property). For compiler optimizations to change the initial state of a variable that has *no explicitly set initial value* is normal - that is part of one of the optimizations you turned on. I would suggest ALWAYS setting your variables to a known initial variable before use. This has been a software development best practice for as long as I can remember. – quellish Apr 23 '13 at 20:15
  • @quellish I tried initializing the variable inside the constructor, but that didn't help. It still says true inside the object but false outside it and only on the very first object of that class type. Later objects of that class work perfect. – Putz1103 Apr 23 '13 at 20:54
  • Three words: Minimal Test Case. – tc. Apr 24 '13 at 01:34
  • Not sure if this perfectly answers your question, but http://stackoverflow.com/questions/9072688/dot-operator-and-arrow-operator-use-in-c-vs-objective-c – ahwulf Apr 24 '13 at 13:04
  • I don't suppose you create an initial ShellQuestionAnswer when you create your answerObjects array (wherever that it). – Hot Licks Apr 24 '13 at 15:06
  • @HotLicks I have added my full code (in the location of the error). I create 4 objects, add them to an array, then check how many of them say correct. The first time I run this function it returns 0 for correct (although at least one object's `correct` variable is definitely set to true. The second time I run this code (and on) it returns the currect value for amount correct. I have fixed the issue by using properties, but I'm unhappy with not knowing the underlying cause. – Putz1103 Apr 24 '13 at 15:41
  • Please show us your `synthesize` statement. – A-Live Apr 24 '13 at 15:59
  • @A-Live I'm not using a synthesize for this `correct` variable. When I use it as a property everything is fine (using the synthesize), when as a public variable things fail. – Putz1103 Apr 24 '13 at 16:05
  • Then I can't see why it's working with getters as automatic synthesize creates ivar with a leading underscore for the property. – A-Live Apr 24 '13 at 16:29

0 Answers0