Feeds:
Posts
Comments

Archive for August, 2010

Magic box

I spent some time today trying to understand why the valid frame rectangle argument passed into the initWithFrame: method of my UIScrollView was being transformed into a null frame parameter once inside the method when viewed in the debugger.

Or, for those who have no idea what I just said, I pushed a rectangular frame through a slot in a box and when I looked inside the box to retrieve the frame, the frame had vanished! Now, I like conjuring tricks as much as the next person, but not during the execution of my programs!

I decided to run one of Apple’s sample programs that also uses a UIScrollView to see if it too was performing this mysterious conjuring trick, but it wasn’t. I then morphed my code to more closely resemble that of Apple’s, but still mine didn’t work.

Then I checked the build info to see if Apple’s program was being built differently from mine… and it was! Mine was using the LLVM 1.5 compiler and Apple’s was using the GCC 4.2 compiler. (The compiler converts all my human-readable code into machine-readable code – code that the iPhone will understand.)

So, I changed the compiler, rebuilt my program and… hey presto! As if by magic, the rectangular frame reappeared inside the box.

Hmm… you know what’s worse than having a bug in your program?

Having a bug in your compiler!

Advertisements

Read Full Post »

Last night I didn’t get to bed until after 2am. The reason? I decided to test the new level select screens on the iPad simulator, only to find that the background map would not display.

So, I ran it in the iPhone simulator to make sure it worked there, and it did. I cleaned all targets and cleared the cache, but the map still didn’t appear on the iPad simulator. I tried using an image used by one of the XIB files, but that didn’t work. I looked at all the settings for the project and target, played around with directory paths, but to no avail. And I searched the Internet to see if anyone else was having this problem, but no one seemed to be.

At this point, it was pretty late and so I came to the conclusion that I must have overlooked something very simple. Even though I had no plans to actually post my predicament on any forums, I mentally went through the process of how I would phrase it and what code I would use to exemplify it. It was at that point that the penny dropped.

The name of the map is in a property list, it’s read into a variable and it’s used as the filename for the map’s image. However, I realised that the resulting filename didn’t have an extension (.png), which explained why the iPad simulator was complaining that it couldn’t find the image. But hang on… the iPhone simulator didn’t complain and displayed the image anyway!

So, it would seem that even though the iPhone simulator seemed to be working, in fact it wasn’t, because it should have complained that the image didn’t exist. And the iPad simulator seemed not to be working as it didn’t show the image, but in fact it was, because it was acting according to the proper specification. :-/

Read Full Post »

Ironically, for an article about concise error reporting, this entry is rather long. But don’t worry, I’ve given you a ‘get out of jail free card’ a few paragraphs down, if you wish to use it! :p

I added some code today which reads game level data from a property list. At some point between now and when Animal Tracker is submitted to the App Store, it’s entirely possible, even probable that there will be errors in the data. As a result, I need to check all the data as they are read in and log any errors that may have occurred.

Now, that in itself isn’t a problem. It’s just that if I’m not careful, I’ll be writing as many lines of error checking code for that part of the program as there are lines of actual game code, making it difficult to see the proverbial wood for the trees.

Another factor to consider is that when the game is actually running on an iDevice, any error logs that are generated never get seen and just slow the game down. So, ideally, when the game is compiled for a release build (where it will run on an actual iDevice, as opposed to a debug build, where it will just run in a simulator on the Mac), there shouldn’t be any error logs generated at all.

Well, what follows is quite technical (although I’ve endeavoured to explain things for the non-technically minded), so if you’re not into all that stuff, I’ll forgive you if you stop reading now… and I’ll see you next time! lol

And for those of you who already know about programming or who would like to learn a little, read on! 🙂

Here’s an example of what I’m talking about:

if ((regionName = [regionDictionary objectForKey:@"Name"])) {
[newRegion setName:regionName];
} else {
NSLog(@"[RootViewController parseRegions] Region %d: 'Name' key not found", i);
}

Firstly, don’t worry about trying to understand what the code does as that’s not important. What is important, however, is that the first three lines are actually doing some useful work, but the last three lines only log an error should one occur – and even then, only when the game is run on the simulator on the Mac.

So that the log code disappears when the game is built to run on an actual iDevice, I changed the code to:

if ((regionName = [regionDictionary objectForKey:@"Name"])) {
[newRegion setName:regionName];
}
#if DEBUG
else { NSLog(@"[RootViewController parseRegions] Region %d: 'Name' key not found", i); }
#endif

To understand the last three lines, I need to introduce the ‘preprocessor’. It’s a utility which looks through your program code for special preprocessor commands and then alters that code depending on whether certain conditions have been met.

The ‘#if’ and ‘#endif’ keywords are preprocessor commands, which in this case tell the preprocessor to only include the ‘else’ command in the final program if we’re in DEBUG mode. When in RELEASE mode (for running on an actual iDevice), the ‘else’ line isn’t included in the program at all, resulting in a smaller executable program. The down side now though is that I’ve doubled the number of lines of code in the text editor.

What I need, then, is to boil those last three lines down to just one, which I chose to do so as follows:

if ((regionName = [regionDictionary objectForKey:@"Name"])) {
[newRegion setName:regionName];
}
ELSE_LOG1(@"[RootViewController parseRegions] Region %d: 'Name' key not found", i)

First, I need to introduce the concept of a ‘macro’. It’s really just a copy and paste mechanism that is performed by our friend the preprocessor. Here, the macro called ‘ELSE_LOG1’ takes two arguments (objects which are passed to the macro for later use). What it actually does, we don’t know yet, because I haven’t defined it yet, so I’ll do that now:

#ifdef DEBUG
#define ELSE_LOG1(string,a) else { NSLog(string, a); }
#else
#define ELSE_LOG1(string,a)
#endif

What this basically says to the preprocessor is, ‘If we’re in DEBUG mode, replace every occurence of ‘ELSE_LOG1’ you find in the program with the code ‘else { NSLog(string, a); }, but instead of writing ‘string’, write the first object that was passed in to the macro, and instead of ‘a’, write the second object passed in (as separated by a comma)’.

So, in our case, ‘string’ will be replaced with:

@"[RootViewController parseRegions] Region %d: 'Name' key not found"

and ‘a’ will be replaced with:

i

In DEBUG mode, then, the preprocessor will effectively expand our code from:

ELSE_LOG1(@"[RootViewController parseRegions] Region %d: 'Name' key not found", i)

to:

else { NSLog(@"[RootViewController parseRegions] Region %d: 'Name' key not found", i); }

The last three lines of the preprocessor code tell the preprocessor, ‘If we’re not in DEBUG mode, replace all occurences of the ‘ELSE_LOG1’ macro (and its arguments) with… nothing! Nada. Empty space. This effectively removes the whole line from the program when it’s built in any mode other than DEBUG (i.e. RELEASE)… which is what we want!

So, the original code:

if ((regionName = [regionDictionary objectForKey:@"Name"])) {
[newRegion setName:regionName];
}
ELSE_LOG1(@"[RootViewController parseRegions] Region %d: 'Name' key not found", i)

… in DEBUG mode becomes:

if ((regionName = [regionDictionary objectForKey:@"Name"])) {
[newRegion setName:regionName];
}
else { NSLog(@"[RootViewController parseRegions] Region %d: 'Name' key not found", i); }

… and in RELEASE mode becomes:

if ((regionName = [regionDictionary objectForKey:@"Name"])) {
[newRegion setName:regionName];
}

Well, congratulations if you’ve made it this far… woohoo! 🙂 See you next time!

Read Full Post »