Oops

So much for kicking off the iPad game series today. In a fit of nostalgia I fired up the old NeXT slab and marvelled at how slow it was just loading TBL’s web browser. I thought I’d dabble with Project Builder too, but it seemed to hang when I fired it up. So I spent he afternoon looking at this:

Turns out there was no need to reinstall – I’d just forgotten how incredibly SLOW ProjectBuilder was at loading. I mean, we’re talking 5 minutes here. On a pretty high spec slab (a whopping 32Mb).

So, back to the real world… game stuff coming soon.

Posted in Rants and random | Leave a comment

Lethargy

I’m cooling off in the back garden after going for my first run in over two months (ha! Who needs a 3G iPad when you’ve already got an iPhone witha personal hotspot?). I’d like to be able to say that I like running, but the sad fact is that I do it simply because I’d turn into a gutbucket if I didn’t. So today I managed to force myself to hobble round the 5k route that I “usually” run, and as usual when running I got bored and my mind started wandering off to that strange alternate reality where I don’t have to work for anyone but myself.

It’s all well and good dreaming about this shit, but unless you actually get off your arse and do something it’ll never happen, right? So, after interviewing myself for various mags and awards I puffed past McDonalds and realised that I ought to get real and just get another app on the AppStore for starters.

Specifically, my game.

Games are what got me into software development in the first place, and although I know this game won’t make me rich or even get me an interview in the Bath Chronicle I still want to play it. The iPhone version is pretty much complete apart from the graphics, but I seem to want to rewrite it for the iPad, probably just because it’s my new toy.

I’m in danger of not seeing the project through, of course, so I’ve decided to go public and write a developer log here. Not really a tutorial, as I’m bound to make mistakes along the way and disappear down blind alleys, but the fact that it’s out here on the net will hopefully remind me that my loving audience is waiting with bated breath for the next installment, and hence keep me going when I run out of steam.

The Game AI port is part of this app – well, the steering behaviour stuff is, anyhow – so that’ll appear eventually when I get to implementing the enemies. In the meantime, brace yourself for The Idea – coming right after I go and have a shower and fit a cat flap to the kitchen door.

Posted in iPad game | Tagged | Leave a comment

Exception debugging

I hate it when an app craps out with an uncaught exception. Maybe I’m unlucky, but rarely does the exception message point me at the broken bit of code:

2012-03-31 16:47:24.081 ODW[194:707] *** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘Unacceptable type of value for attribute: property = “scribbles”; desired type = NSData; given type = __NSArrayM; value = (

).’

*** First throw call stack:

(0x358cb88f 0×31244259 0x37522cdd 0x3752451b 0x5c9ab 0x5dda7 0x303e8ad3 0x303e84c1 0x303ce83d 0x303ce0e3 0x355a122b 0x3589f523 0x3589f4c5 0x3589e313 0x358214a5 0x3582136d 0x355a0439 0x303fce7d 0x5716f 0×57114)

terminate called throwing an exception

So how to debug? Simples – just stick a breakpoint on [NSException raise], or use Xcode 4′s “Add Exception Breakpoint…” menu command, found under the tiny barely-noticeable + button on the Breakpoints tab (Cmd-6):

Image

When you select this you get a bunch of useful exception handling options:

Image

Very nifty.

Posted in Xcode | Leave a comment

Smart

Only tenuously related to iOS, this post. Actually it’s just a spout/rant about interviews and has no technical content whatsoever, so probably best to click away now.

Still here? Ok – you asked for it. I was reading this article about Google interviews, which reminded me of an interview for an iOS job that I had recently. Based on other recent interviews I was expecting a good old grilling about my AppStore portfolio (demoing my apps, answering why-did-you-do-it-that-way and what-would-you-do-differently-next-time type questions), the woes associated with the AppStore submission process, and the subtleties of Objective-C properties. Not a bit of it – I had a preliminary phone interview where they asked a handful of ludicrously simple Objective-C questions (“what do you use @synthesize for?”) and then was invited along for a face-to-face. 

The face-to-face started off reasonably well. The prospective boss asked me a bunch of stuff about how I’d lead a team and what my perceived strengths and weaknesses were – the usual stuff, in other words. Then a couple of techies came in and I steeled myself for some full-on iOS/Objective-C questions.

Not a bit of it.

It turned into a Google Interview. The first question was about dropping eggs off the top of a tower block, and it went downhill from there. 

Now, call me old-fashioned, but I’d have thought the best way of assessing whether somebody is suitable for a role is to get inside their head and find out their motivations for wanting to do the job – how keen are they on iOS? Will they stay up-to-date with developments in what is a very fast-paced world, or will they kick back once they’ve got the job and rely solely on company-provided training? Do they have That Look in their eyes when they talk about the tech? And do they actually know how this stuff really works? Can they frig the runtime to reroute messages? Can they track down a memory leak? Would they know how to debug those obscure Core Data errors? Do they know the difference between a pointer and a reference, and do they care?

No, in this particular company they seemed only to value lateral thinking and the ability to not go “Eh? WTF?” when somebody asks a bizarre question.

Needless to say I didn’t get the job, but then again it didn’t seem to occur to them that the interview was as much about them convincing me to work for them as the other way round. They failed, utterly. After half an hour I was ready to get up and walk out, but morbid fascination kept me in my seat – what the hell are they going to ask next? By the third question I found it almost impossible to focus on what it was they were asking me – I kept thinking to myself ‘what the hell has this got to do with iPads?’ and ‘are you serious?’

At the end of the interview they asked me if I had any questions or comments. The only polite thing I could think of asking was how many other iOS developers they’d managed to hire. The answer: “errrr, well, none actually…”

I had to laugh.

Posted in Rants and random | Tagged | 2 Comments

Xcode 4.3.1 woes

Xcode seems to be getting flakier with each new release. Up until recently I never really minded those occasional crashes and hangs, but now that I’m using Xcode all day every day it’s starting to really, ahem, bug me.

The latest version doesn’t seem to like certain old nib files – not all of them, but some of them. Sadly, I don’t know exactly what it’s choking on yet, but when I fire up IB and make a change to a dodgy nib I see something like this:

ibtoold(2800,0x7fff73f4b960) malloc: *** error for object 0x7f9cae80cc08: incorrect checksum for freed object - object was probably modified after being freed.
*** set a breakpoint in malloc_error_break to debug
Command /Applications/Xcode.app/Contents/Developer/usr/bin/ibtool failed with exit code 255

The usual clean & rebuild fix doesn’t work here. Eventually it either clears itself up (seriously, it just goes away after a few rebuilds!) or Xcode crashes. It got so bad this afternoon that I couldn’t even restart Xcode – it had got itself into such a mess that it just beachballed every time I tried to launch it. Log out/restart didn’t help, nor did deleting the com.apple.dt.Xcode stuff in ~/Library/Preferences (my usual desperate fix for Xcode weirdness).

After a good couple of hours of pratting about I worked out that the problem lay in the UserInterfaceState.xcuserstate file in the workspace. Moved it to UserInterfaceState.xcuserstate.bak, restarted Xcode, and it came back to life. Even managed to build my project without the ibtool failure. Whoa.

I’m feeling old – this is reminding me of the bad old days of trying to develop on Windows 3.1!

Posted in Xcode | Tagged | 3 Comments

Core Data and SQL debugging

The latest app uses Core Data to manage a small database (only four entities in the model) for keeping track of a set of documents and related stuff. I was dreading having to really get to grips with Core Data, having skirted around it a few times in the past and been put off by the huge volume of very dull-looking databasey documentation that surrounds it.

Shouldn’t have worried. It’s a doodle. And actually almost fun to use! Yep, databases and fun are just about compatible, after all.

Quick summary, as I’m bound to forget the ins and outs very rapidly – I’ve got to get back to the game programming stuff, it’s getting itchier by the week and is definitely overdue a good scratch.

There are four main objects in the Core Data “stack”:

  • the PSC (“persistent store controller”) – the connection to the underlying database, implemented by NSPersistentStoreController
  • the MOM (“managed object model”) – the database schema, implemented by NSManagedObjectModel
  • the MOC (“managed object context”) – the in-memory working copy of the database, implemented by NSManagedObjectContext
  • the FRC (“fetched results controller”) – used to obtain sorted lists of a given entity, implemented by NSFetchedResultsController
  • MOs (“managed objects”) – the actual data, implemented by NSManagedObject

Generally you have single instances of the first three living in some convenient central location (the AppDelegate, in other words), implemented as readonly lazily-instantiated properties.

The PSC is initialised with the the URL of the database, e.g.

dbURL = [NSURL fileURLWithPath:[[self applicationDocumentsDirectory] 
             stringByAppendingPathComponent: @"ODW.sqlite"]];

You use the data modelling tool in Xcode to build up the MOM, and you make it accessible to code like this:

mom = [NSManagedObjectModel mergedModelFromBundles:nil];

The MOC is created using the PSC:

  NSPersistentStoreCoordinator *psc = [self persistentStoreCoordinator];
  if (psc != nil) {
    moc = [[NSManagedObjectContext alloc] init];
    [moc setPersistentStoreCoordinator:psc];
  }

To obtain a set of entities, use an FRC. First define a getter the same way as for the PSC, MOM and MOC:

- (NSFetchedResultsController*)notesFRC {
    if (notesFRC != nil) {
      return notesFRC;
    }

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Note" inManagedObjectContext:managedObjectContext];
    [fetchRequest setEntity:entity];

     NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"projectref" ascending:YES];    
    [fetchRequest setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]]; 

    NSFetchedResultsController *frc = [[NSFetchedResultsController alloc] 
        initWithFetchRequest:fetchRequest 
        managedObjectContext:managedObjectContext
        sectionNameKeyPath:nil
        cacheName:@"Notes"];
    frc.delegate = self; 
    notesFRC = frc;
    return notesFRC;
}

Next, use the FRC’s fetchedObjects property:

- (void)showNote:(Note *)note {
    NSLog(@"ViewController.showNote: %@ %@", note.projectref, note.timestamp);
    NSArray* notes = self.notesFRC.fetchedObjects;
    if ([notes containsObject:note]) {
        [self showNoteViewForNote:note];
    } else {
        NSLog(@"ARGH - couldn't find note: notes=%@", notes);
        abort(); // dunno what to do about this yet
    }
}

Piece of cake, isn’t it? OK, the error handling leaves a bit to be desired, but you get the idea…

To insert new entities into the database you need to get the entities’ descriptions from the MOC and tell the MOM to create new instances. Any relationships between entities need to be set up:

    // create a new Note 
    NSEntityDescription *noteDesc = [NSEntityDescription entityForName:@"Note" inManagedObjectContext:self.moc];    
    Note *note = [NSEntityDescription insertNewObjectForEntityForName:[noteDesc name] inManagedObjectContext:self.moc];
    note.client = [UserDefaults instance].defaultClient;
    note.projectref = [[UserDefaults instance] makeNewDefaultProjectRef];
    note.subject = [UserDefaults instance].defaultSubject;
    note.timestamp = [NSDate date]; 

    // note.present is derived from the relationship between the Note and Person tables
    NSEntityDescription *personDesc = [NSEntityDescription entityForName:@"Person" inManagedObjectContext:self.moc];
    Person* person = [NSEntityDescription insertNewObjectForEntityForName:[personDesc name] inManagedObjectContext:self.moc];
    person.name = [UserDefaults instance].defaultPerson;
    [note addPresent:[NSSet setWithArray:[NSArray arrayWithObject:person]]];
    [person addNotes:[NSSet setWithArray:[NSArray arrayWithObject:note]]]; 

    NSError *error = nil;
    if (![self.moc save:&error]) {
        NSLog(@"ERROR saving new Note: %@", [error localizedDescription]);
        abort();
    }

The Note and Person classes are derived from NSManagedObject and generated by Xcode. Any changes to instances of these classes result in the FRC’s delegate being informed:

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    NSLog(@"ViewController.controllerDidChangeContent: %@", controller);
    // save the changes
    NSError* error = nil;
    if (![moc save:&error]) {
        NSLog(@"ERROR saving changes to database: %@", [error localizedDescription]);
        abort();
    } else {
        NSLog(@"DATABASE WAS UPDATED");
    }
}

And that’s pretty much it for database access via code.

A couple of handy debug hints:

1. To see what’s actually going on with the underlying SQLite database, add the following command-line argument to the Run scheme:

-com.apple.CoreData.SQLDebug 1

2. To see what’s actually in the underlying SQLite database, fire up sqlite3 against the file. This is easier to do using the Simulator, as all you have to do is cd to your app’s Documents directory and run sqlite3 against the file. Use the .dump command to see what’s in the database:

blaptop2:~ al $ cd /Users/al/Library/Application\ Support/iPhone\ Simulator/5.0/Applications/16F83964-62D2-43BB-80DA-08FFD528DEC5/Documents
blaptop2:Documents al$ sqlite3 ODW.sqlite 
SQLite version 3.7.7 2011-06-25 16:35:41
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> .dump
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE ZNOTE ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZPEN INTEGER, ZTIMESTAMP TIMESTAMP, ZCLIENT VARCHAR, ZPROJECTREF VARCHAR, ZSUBJECT VARCHAR );
INSERT INTO "ZNOTE" VALUES(1,1,10,NULL,350514572.612056,'Bobbins plc','JAWZ002','Wobblage');
INSERT INTO "ZNOTE" VALUES(2,1,18,NULL,350564320.877214,'Bobble','JAWZ001','Silliness');
INSERT INTO "ZNOTE" VALUES(3,1,21,NULL,350564456.552048,'cats','JAWZ101','cat flap thing');
CREATE TABLE Z_1PRESENT ( Z_1NOTES INTEGER, Z_3PRESENT INTEGER, PRIMARY KEY (Z_1NOTES, Z_3PRESENT) );
INSERT INTO "Z_1PRESENT" VALUES(1,1);
INSERT INTO "Z_1PRESENT" VALUES(1,3);
INSERT INTO "Z_1PRESENT" VALUES(2,5);
INSERT INTO "Z_1PRESENT" VALUES(3,8);
INSERT INTO "Z_1PRESENT" VALUES(3,9);
INSERT INTO "Z_1PRESENT" VALUES(3,10);
INSERT INTO "Z_1PRESENT" VALUES(3,12);
INSERT INTO "Z_1PRESENT" VALUES(2,13);
CREATE TABLE Z_1TODO ( Z_1NOTE INTEGER, Z_4TODO INTEGER, PRIMARY KEY (Z_1NOTE, Z_4TODO) );
CREATE TABLE ZPEN ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZPENWIDTH INTEGER, ZPENCILTYPE VARCHAR, ZPENCOLOUR BLOB );
CREATE TABLE ZPERSON ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZNAME VARCHAR );
INSERT INTO "ZPERSON" VALUES(1,3,1,'Al Pearson');
INSERT INTO "ZPERSON" VALUES(2,3,2,'Fred Bloggs');
INSERT INTO "ZPERSON" VALUES(3,3,1,'Sid');
INSERT INTO "ZPERSON" VALUES(4,3,2,'Al Pearson');
INSERT INTO "ZPERSON" VALUES(5,3,1,'Fred Flintstone');
INSERT INTO "ZPERSON" VALUES(6,3,2,'Barney Rubble');
INSERT INTO "ZPERSON" VALUES(7,3,2,'Al Pearson');
INSERT INTO "ZPERSON" VALUES(8,3,1,'Flo');
INSERT INTO "ZPERSON" VALUES(9,3,1,'Mr Norris');
INSERT INTO "ZPERSON" VALUES(10,3,3,'me');
INSERT INTO "ZPERSON" VALUES(11,3,2,'Al');
INSERT INTO "ZPERSON" VALUES(12,3,1,'Will');
INSERT INTO "ZPERSON" VALUES(13,3,1,'Wilma Flintstone');
CREATE TABLE ZTODO ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZTIMESTAMP TIMESTAMP, ZDETAILS VARCHAR );
CREATE TABLE Z_PRIMARYKEY (Z_ENT INTEGER PRIMARY KEY, Z_NAME VARCHAR, Z_SUPER INTEGER, Z_MAX INTEGER);
INSERT INTO "Z_PRIMARYKEY" VALUES(1,'Note',0,3);
INSERT INTO "Z_PRIMARYKEY" VALUES(2,'Pen',0,0);
INSERT INTO "Z_PRIMARYKEY" VALUES(3,'Person',0,13);
INSERT INTO "Z_PRIMARYKEY" VALUES(4,'ToDo',0,0);
CREATE TABLE Z_METADATA (Z_VERSION INTEGER PRIMARY KEY, Z_UUID VARCHAR(255), Z_PLIST BLOB);
INSERT INTO "Z_METADATA" VALUES(1,'4C7BE92C-6C67-4B81-AD20-2E683F8CD647',X'62706C6973743030D601020304050607090A1314155F101E4E5353746F72654D6F64656C56657273696F6E4964656E746966696572735F101D4E5350657273697374656E63654672616D65776F726B56657273696F6E5F10194E5353746F72654D6F64656C56657273696F6E4861736865735B4E5353746F7265547970655F10125F4E534175746F56616375756D4C6576656C5F10204E5353746F72654D6F64656C56657273696F6E48617368657356657273696F6EA10850110182D40B0C0D0E0F10111254546F446F5350656E56506572736F6E544E6F74654F102050D605BDF65E5153589B7E7D6A7ACBA3AF5EC01FF74F4FBCDCF1338B4FA405474F102082DF4AEE8DB079BF78A8C8992C1528B0990FD0FA9CF10C2FA397473A6A05B7BD4F1020241ECFCF14A741B7EBA752DF9212490FAB646C462BC4B03649CB41C0394830B24F1020F2F8F869CBDB18B7B600B1C35CFCC4FB5766D322D8C220309E275770068750485653514C6974655132100300080015003600560072007E009300B600B800B900BC00C500CA00CE00D500DA00FD012001430166016D016F0000000000000201000000000000001600000000000000000000000000000171');
CREATE INDEX ZNOTE_ZPEN_INDEX ON ZNOTE (ZPEN);
COMMIT;
sqlite> .quit
blaptop2:Documents al$
Posted in Core Data | Leave a comment

EXC_BAD_ACCESS and iOS5

My old enemy EXC_BAD_ACCESS reared his ugly head again the other day. I’d thought I’d seen the last of him now that I’m working with iOS5 and ARC – no more manual retain/release nonsense, so how could I over-release anything?

To keep a long and painful story short and painless, it was down to me manually replacing a subview of the main view and expecting a bit much of KVO (I’d set things up to observe a control in the subview, and somehow when replacing the subview it took a while for the news to bubble through to the KVO classes, manifesting itself in a nasty EXC_BAD_ACCESS crash down in NSKVOPendingNotificationCreate).

Anyhow, the point of the post isn’t to waffle on about KVO – it’s to recapture for next time (hopefully never, but I’m not that naive) that little debug trick that I used to take for granted, yet have somehow managed to forget in the space of 6 months: NSZombieEnabled.

Yup – took me a big of digging around the net to remember this one – just set the NSZombieEnabled environment variable to YES and any messages to over-released objects are reported so that you can see where things are getting weird. Fire up Instruments and use the Allocations tool to track the retain/release lifecycle of the class in question, and that’ll show you where your problem lies.

Yeah, I also got a bit stuck trying to find the environment variables bit in Xcode 4, too :-) It’s in the “Run” scheme (Product->Edit scheme...), “of course”. Wow, is it really that long since I needed to do customise an app’s startup? :-)

Posted in iOS | Leave a comment