views:

1004

answers:

4

Being a somewhat newbie to iPhone development I started debugging some code attempting to find my mistake. When I attempt to p tlEntries from the debugger I get the <variable optimized away by compiler> message while stopped on the if statement. The following is my code:

NSArray *tlEntries = [[NSArray alloc] initWithArray:[self fetchJSONValueForURL:url]];
for (NSDictionary *info in tlEntries) 
{
    if ([info objectForKey:@"screen_name"] != nil)
         NSLog(@"Found %@ in the timeline", [info objectForKey:@"screen_name"]);
}

Earlier debugging gives me confidence the URL is indeed returning a valid NSArray, but I don't understand why tlEntries is being "optimized away".

+2  A: 

The compiler probably noticed that you only use tlEntries twice in the beginning, and don't use it at all in the loop. Loops create an enumeration object instead of keeping a reference to the container object, if I remember correctly. So tlEntries should be valid for the first line but then gone for the rest.

Idea: You can force the compiler to keep it by using tlEntries somewhere later in the function. Something like

NSPrint(@"IGNORE THIS LINE %p", tlEntries);

Better idea: You can set the optimization to -O0. This is highly recommended for debugging code. It should be set to -O0 automatically if you use the "Debug" instead of "Release" builds, but you can change that.

Dietrich Epp
The 'for .. in' syntax actually generates a loop which calls methods on tlEntries fairly (once for every n elements within the array), so from what I can see the compiler shouldn't have optimized it away because of that.
Jim Dovey
+11  A: 

The proper solution is to declare the variable in a different way as follows:

volatile NSArray *tlEntries;

Indeed, the volatile keyword is used exactly to inform the compiler that it must not try to optimize the code related to that variable in any way. Kind regards.

unforgiven
I really do not understand the negative vote here. I give an answer which is correct and solves the problem as astated (try searching the Objective C documentation or use google to find the exact meaning of the volatile keyword in Objective C, C, and C++).
unforgiven
me neither. +1
kent
Seriously - don't knock yourself out about random downvotes.
Abizern
Thank you Kent and Abizern. When one spends some time answering a question, in the hope to provide some help and guidance (is not this the ultimate goal of Stack Overflow?), it really hurts to see that it appears as one is wasting his time...
unforgiven
This is the correct solution. Upvoted!
Ben Gotow
+1 correct answer.
Justicle
The syntax here is wrong for what you are trying to do. `volatile NSArray *` declares a non-volatile pointer to a volatile object. You probably meant to write `NSArray *volatile`, which declares a volatile pointer to a non-volatile object. Also, this would not prevent the optimizations that you are trying to prevent. The `volatile` keyword only makes the compiler be strict about the ordering and presence of loads and stores, but it can still eliminate dead variables — `volatile` does NOT mean "don't optimize".
Dietrich Epp
from Programming in Objective C 2.0 page 214: Volatile tells the compiler explicitly that the specified variable will change its value. It’s included in the language to prevent the compiler from optimizing away seemingly redundant assignments to a variable or repeated examination of a variable without its value seemingly changing. regarding the syntax: you declare outPort to be a volatile variable, like this:volatile char *outPort;
unforgiven
+1  A: 

Assuming that you never use this variable later on (which seems reasonable if the compiler optimized it away), one very important way you could use to solve the issue (similar to Dietrich's example, though better for your program) is to do a:

[tlEntries release];

Otherwise, you will definitely leak that memory away. This will make the compiler see the object used later (as does the NSPrint) so it will not be optimized out.

littleknown
+2  A: 

When compiler optimizations are enabled, variables are frequently "optimized away" by placing them in registers or other tricks such as statement re-ordering. If you are using any -O flag other than -O0 then this can take place.

I don't think that adding additional references to the variable in your code are going to prevent this from happening. (If anything, the compiler may try even harder since the potential gain from optimizing it away is greater.)

As a temporary workaround, you can declare the variable "volatile". This isn't generally a good long-term solution because it prevents the compiler from performing any optimizations involving that variable.

Another workaround is to use good old-fashioned logging. Something like:

NSLog(@"Entries initialized as: %@", tlEntries);

Finally, you can also compile with -O0. Many people recommend this for the Debug project profile. Although it prevents optimization it makes debugging much easier. Stepping through statements is actually predictable, and variables can be viewed. Unfortunately it has what I consider a rather nasty side-effect with the version of gcc that Apple ships: when -O0 is in effect you can't get warnings about using variables prior to initialization. (I personally find this warning useful enough that I'm willing to suffer the pain of debugging being less convenient.)

P.S. You have a memory leak in the snippet posted; for clarity an additional line should be added:

[tlEntries release];
Andrew