Tuesday, April 21, 2009

GTM test suite for the iPhone - figuring out why it was hanging on startup

[Sorry about the formatting problems. This was posted to my old blog, and I found it again when I was doing a google search for the topic. The old blog posts are lying around somewhere, but I'm lazy so I'll just cut-and-paste from the google cache...] I'm trying to use the GTM test suite, and it's just hanging. The build window shows:
    2008-11-21 09:23:46.996 UnitTests[1794:10b] CFPreferences: user home directory at /Users/jamesmoore/dev/TestPlatform/build/TestPlatform.build/Debug-iphonesimulator/UnitTests.build/iPhone Simulator User Dir is unavailable. User domains will be volatile.    2008-11-21 09:23:47.012 UnitTests[1794:10b] Unable to send CPDistributedMessagingCenter message named SBRemoteNotificationClientStartedMessage to com.apple.remotenotification.server: (ipc/send) invalid destination port
Starting under the debugger (using option-cmd-Y to skip running the test harness shell script) isn't very informative:
(gdb) bt#0  0x964954a6 in mach_msg_trap ()#1  0x9649cc9c in mach_msg ()#2  0x956fd0ce in CFRunLoopRunSpecific ()#3  0x956fdcf8 in CFRunLoopRunInMode ()#4  0x31699d38 in GSEventRunModal ()#5  0x31699dfd in GSEventRun ()#6  0x30a5dadb in -[UIApplication _run] ()#7  0x30a68ce4 in UIApplicationMain ()#8  0x00002a39 in main (argc=1, argv=0xbffff060) at /Users/jamesmoore/dev/TestPlatform/../google-toolbox-for-mac/UnitTesting/GTMIPhoneUnitTestMain.m:30(gdb)
All that looks fine. So, did the tests run at all? GTMIphoneUnitTestMain.m does:
  int retVal = UIApplicationMain(argc, argv, nil, @"GTMIPhoneUnitTestDelegate");
So I set a breakpoint on the first line of applicationDidFinishLaunching, on the call to runTests:
- (void)applicationDidFinishLaunching:(UIApplication *)application {  [self runTests];
And sure enough, it's not hit. So my next suspect is the app delegate for the regular application - inthis case, TestPlatformAppDelegate. Set a breakpoint in itsapplicationDidFinishLaunching, start up, and sure enough that's what'srunning. Why? Turns out I had added MainWindow.xib to the unit test target. For the regular (non-unit-test)application, MainWindow.xib has an instance of TestPlatformAppDelegate.The TestPlatformAppDelegate is attached to the File's Owner delegate.When the nib file is instantiated, it creates theTestPlatformAppDelegate, then sets the File's Owner delegate to be thenewly instantiated TestPlatformAppDelegate. That overwrites theinstance of GTMIPhoneUnitTestDelegate that used to be there, and no tests will run. The next question is can something spit out a warning if this happens? Off the top of my head, I don't know who gets called when nib files areloaded. I'm sitting at a debugger, so I slap in a new init method toGTMIPhoneUnitTestDelegate, set a breakpoint on it, and restart. Here'sthe stack:
#0  -[TestPlatformAppDelegate init] (self=0x44fb80, _cmd=0x96d6c858) at /Users/jamesmoore/dev/TestPlatform/Classes/TestPlatformAppDelegate.m:19#1  0x30c2ca67 in -[UIClassSwapper initWithCoder:] ()#2  0x922a4940 in _decodeObjectBinary ()#3  0x922a42b5 in _decodeObject ()#4  0x30c2c615 in -[UIRuntimeConnection initWithCoder:] ()#5  0x922a4940 in _decodeObjectBinary ()#6  0x922a63cd in -[NSKeyedUnarchiver _decodeArrayOfObjectsForKey:] ()#7  0x922a6849 in -[NSArray(NSArray) initWithCoder:] ()#8  0x922a4940 in _decodeObjectBinary ()#9  0x922a42b5 in _decodeObject ()#10 0x30c2bbeb in -[UINib instantiateWithOptions:owner:loadingResourcesFromBundle:] ()#11 0x30c2dcf8 in -[NSBundle(NSBundleAdditions) loadNibNamed:owner:options:] ()#12 0x30a5df99 in -[UIApplication _loadMainNibFile] ()#13 0x30a65f42 in -[UIApplication _runWithURL:] ()#14 0x922ce5ee in __NSFireDelayedPerform ()#15 0x956fdb45 in CFRunLoopRunSpecific ()#16 0x956fdcf8 in CFRunLoopRunInMode ()#17 0x31699d38 in GSEventRunModal ()#18 0x31699dfd in GSEventRun ()#19 0x30a5dadb in -[UIApplication _run] ()#20 0x30a68ce4 in UIApplicationMain ()#21 0x00002a39 in main (argc=1, argv=0xbffff060) at /Users/jamesmoore/dev/TestPlatform/../google-toolbox-for-mac/UnitTesting/GTMIPhoneUnitTestMain.m:30
The interesting points look like:
#10 0x30c2bbeb in -[UINib instantiateWithOptions:owner:loadingResourcesFromBundle:] ()#12 0x30a5df99 in -[UIApplication _loadMainNibFile] ()
Does one of those have something to hook into? I start reading doc. The Resource Programming Guide has lots ofinformation about nib files, but the only thing I find that seemsrelevant is the bit where it points out that nib instantiation is goingto use setValue:forKey: to set the fields. To see what happens there, I added this to GTMIphoneUnitTestMain.m:
@interface GuardedUIApplication : UIApplication@end @implementation GuardedUIApplication- (void)setValue:(id)value forKey:(NSString *)key{ NSLog(@"Current value for key |%@| is |%@|", key, value); [super setValue: value forKey: key];}@end
And changed the call to UIApplicationMain to use GuardedUIApplication instead of the default UIApplication:
  int retVal = UIApplicationMain(argc, argv, @"GuardedUIApplication", @"GTMIPhoneUnitTestDelegate");
Running it again, I see that the call I expected does in fact happen:
2008-11-21 10:43:09.966 UnitTests[2841:20b] Current value for key |delegate| is ||
Something like this might be a solution:
@interface GuardedUIApplication : UIApplication@end@implementation GuardedUIApplication- (void)setValue:(id)value forKey:(NSString *)key{ NSLog(@"Existing value for key |%@| is |%@|", key, [self valueForKey: key]); NSLog(@"New value for key |%@| is |%@|", key, value); if ([key isEqualToString: @"delegate"] && ![value isKindOfClass: [GTMIPhoneUnitTestDelegate class]]) {  NSException *e = [NSException failureInFile: [NSString stringWithCString: __FILE__]             atLine: __LINE__          withDescription: @"The app delegate must be a GTMIPhoneUnitTestDelegate.  Is MainWindow.nib attempting to use a different application delegate?"];  [e raise]; } [super setValue: value forKey: key];}@end

Monday, April 13, 2009

Objective-C basics for C programmers

Objective-C is C

First, if you want to learn Objective-C and you don’t know C yet, go learn C.  Come back when you’re done.  Objective-C is a set of addons to C – you must know C first.  C is a small language, and it won’t take you that long.  (Learning to use C well is a different problem, of course.)

What to read

The Apple doc for Objective-C is pretty good, but it gets bogged down in places because it's not assuming you already know C.  It's also written as if Objective-C were a new language, instead of just some extra bits on top of C.

(And Chapter 11 of that Apple doc tosses in this shocker: C++ is also included in Objective-C.  It's not usually necessary to use C++ on the iPhone, but it does mean you can use lots of existing C++ code if you need it.)

This article is a stripped-down intro to thinking of Objective-C basics in terms of what they are in C.

Square braces are just macros

The best way to think about Objective-C syntax is that it’s just a new flavor of preprocessor for C.  There are some basic things you care about that are going to spit out normal C, eventually:
  • Messages.  They’re sent using square braces like this: [targetObject theMessage]
  • Class definitions.  They’re blocks that start with @interface @implementation @protocol and end with @end.
  • Selectors.  Selector is the Objective-C term for what most languages call “messages.”  They’re what you send to objects.  (Thinking of them as method invocations is mostly wrong and will get you in trouble later on.)  You’ll see selectors used like this: @selector(theMessage) and in square braces like this: [targetObject theMessage] - that’s just syntatic sugar.  They’re going to compile down to the same thing.

Classes

Classes are structs with some extra fields tacked on.  Here's a trivial class declaration:

@interface SmallClass : NSObject
- (void) helloWorld;
@end

This lays out the class for the compiler.  It allows the compiler to give you useful warnings if you try to send the helloWorld message to this class in the wrong way (so you'll get a warning if you try to call it with a parameter, for example).  It adds a field that's a pointer to the class itself, so at runtime you can dereference the pointer to the class type to figure out which functions correspond to which selectors.   (And yes, this implies that at runtime a class can change its type.  That's nasty, and is used by things like key-value observing.)

The implementation for this would be:

@implementation SmallClass
- (void) helloWorld
{
  NSLog(@"Hello world.");
}
@end
     
The compiler turns this into a helloWorld function that looks something like:

void helloWorldFunctionImplementation (SmallClass *self) {
  NSLog(@"Hello world");
}

To create an instance of SmallClass and send it the helloWorld selector, you'd use the the square brackets syntax:

SmallClass *sm = [[[SmallClass alloc] init] autorelease];
[sm helloWorld];


(For now, ignore the alloc/init/autorelease bits - they're memory management on the iPhone.)

Square brackets are just syntatic sugar that makes sending selectors to objects more pleasant.  What you end up with after square-bracket-procesing is a call to the function objc_msgSend(receiver, selector).  Calling objc_msgSend without the square brackets would do exactly the same thing:

objc_msgSend(sm, @selector(helloWorld));  // same as [sm helloWorld]

Or the same thing in a function:

void sendHelloWorldByHand (SmallClass *target)
{
  objc_msgSend(target, @selector(helloWorld));
}

Notice that the call to @selector(helloWorld) couldn't care less about whether or not there's a method called helloWorld implemented by that object.  It's completely irrelevant - selectors just get translated to numbers by the compiler, and they're not tied to classes in any way.  @selector(helloWorld) becomes an int inside the compiler, and you can send that int to any instance of any class.  Whether or not the target does anything interesting with that message is an unrelated issue.

To put it another way: selectors aren't tied to particular objects; the selector for helloWorld on SmallClass is the same selector that you might send to AnotherClassIHaventImplementedYet.  Selectors are just a lookup mechanism so humans don't have to remember numbers - they get informative tokens to put in code.  But they're just going to turn into numbers when your code is running.

(And you should notice that the Apple doc says not to call objc_msgSend by hand, although I suspect this is widely ignored.)

Sending messages

objc_msgSend(target, selector) goes through these steps to figure out what to call:
  1. Looking at the hidden field in the struct that points to the class of the object. 
  2. Call the function matching the selector.  Classes have lookup tables that specify the selector-to-function mapping, and if there's a matching selector, that's what's called.  If there's no matching selector, check the parent of the class, and so on until you get to the root class. 
  3. If there's no match for the selector, call forwardInvocation:.
#3 is important – it means that you cannot tell by looking at an object's @interface code whether or not it handles a selector.  That decision can't really be made until you're actually sending the selector to the object.  (And since forwardInvocation: is free to do anything it feels like with a selector, there's no guarantee that a selector will do anything the same way twice.)

Monday, April 6, 2009

F# - Converting sequences to lists on the fly

There was a question on StackOverflow about chunking sequences to arrays.  The poster wanted to have the input put into three-element arrays, so something like this:

> seq { for i in 1..10000 -> i} ;;
val it : seq<int> = seq [1; 2; 3; 4; ...]

would become:

> seq { for i in 1..10000 -> i} |> Seq.in_groups_of_n 3;;
val it : seq<int list>
= seq [[1; 2; 3]; [4; 5; 6]; [7; 8; 9]; [10; 11; 12]; ...]
> 

(I usually prefer to have my data in lists rather than arrays, so this isn't quite the answer they were looking for, but it's a trivial conversion from lists to arrays.)

My first solution was something more like iter:

  let in_lists_of_length_n n f (ie : seq<'a>) = 
    use e = ie.GetEnumerator()
    let rec next_with_acc acc =
      match e.MoveNext(), acc with
      | true, a when List.length a + 1 = n ->  
        f(List.rev (e.Current :: a))
        next_with_acc []
      | true, _ -> next_with_acc (e.Current :: acc)
      | false, _ ->
        f(List.rev acc)
      ()
    next_with_acc [] 
 
That's OK, but it's pretty limited.  If what you're doing fits into something like iter, it's good enough.

But what about just transforming the sequence into a new sequence?  That seems like a more general solution.  So:

module Seq =  
  let in_groups_of_n n (s: seq<'a>) =
    let rec in_groups_of_n_with_acc n acc (ie: IEnumerator<'a>) =
      seq {
        match ie.MoveNext(), acc with
        | true, a when List.length a + 1 = n -> 
          yield List.rev (ie.Current :: a)
          yield! in_groups_of_n_with_acc n [] ie
        | true, a -> yield! in_groups_of_n_with_acc n (ie.Current :: a) ie
        | false, a when List.length a > 0 -> yield List.rev acc
        | false, a -> ()
      }
    seq {
      yield! in_groups_of_n_with_acc n [] (s.GetEnumerator())
    }    

That's pretty good - it gets us to the point where we're returning a real sequence, so it's much more useful than the iter-style solution.

But why limit ourselves to lists-of-length-n?  Let's turn the accumulator into a function, so you can get any groups you want:

module Seq =  
  let grouped_by f (s: seq<'a>)=
    let rec grouped_by_with_acc (f: 'a -> 'a list -> 'a list option * 'a list) acc (ie: IEnumerator<'a>) =
      seq {
        if ie.MoveNext()
        then 
          let nextValue, leftovers = f ie.Current acc
          if nextValue.IsSome then yield nextValue.Value
          yield! grouped_by_with_acc f leftovers ie
        else
          if not acc.IsEmpty then yield acc
      }
    seq {
      yield! grouped_by_with_acc f [] (s.GetEnumerator())
    }    

This takes a function that accepts:
  • The next item in the sequence
  • Leftovers from the last run
And returns a tuple:
  • An optional value to yield
  • Leftovers
Here's how its used:

module TestGroupedBy =
  let GroupsOfN n newValue acc =
    let newList = newValue :: acc

    // If we have the right length, return
    // a Some as the first value.  That'll 
    // be yielded by the sequence.
    if List.length acc = n - 1
    then Some (List.rev newList), []
    // If we don't have the right length,
    // use None (so nothing will be yielded)
    else None, newList   
    
  // Note that we're going to pass a curried function - (GroupsOfN 3) is a function
  // taking two arguments, suitable for passing to grouped_by
  let resultWithInts = seq { for i in 1..100 -> i} |> Seq.grouped_by (GroupsOfN 3)
  
  printfn "int result %A" resultWithInts
  
  let resultWithChars = seq { for i in ['a'; 'b'; 'c'; 'd'; 'e'; 'f'] -> i} |> Seq.grouped_by (GroupsOfN 2)
  
  printfn "char result %A" resultWithChars  

The results are:

int result seq [[1; 2; 3]; [4; 5; 6]; [7; 8; 9]; [10; 11; 12]; ...]
char result seq [['a'; 'b']; ['c'; 'd']; ['e'; 'f']]

Let's use this with a more interesting grouping function. Say we want the colors of all the single cows in a list:

module TestGroupedByWithMatching =
  let Cows newValue acc =
    let newList = newValue :: acc
    match newList with
    | "cow" :: color :: "one" :: t -> Some [color], []
    | a :: b :: t -> None, [a; b]
    | l -> None, newList
    
  let someCows = seq { for i in ["one"; "red"; "cow"; "and"; "one"; "white"; "cow"; "and"; "three"; "green"; "cows"] -> i} |> Seq.grouped_by Cows
  printfn "some cows are %A" someCows
  
// This prints:  
// some cows are seq [["red"]; ["white"]; ["cows"; "green"]]
// > 

So there's a problem - at the end, we're yielding the accumulator.  That's what we wanted in the previous examples; we were looking for groups of three, plus the last group of 0, 1, or 2 items.  Here, though, if we don't get a match we don't want to see the leftovers.  Let's create a new method that takes another function that specifies what to do with the accumulator:

  let grouped_by_with_leftover_processing f (f2: 'a list -> list<'a> option) (s: seq<'a>)=
    let rec grouped_by_with_acc (f: 'a -> 'a list -> 'a list option * 'a list) acc (ie: IEnumerator<'a>) =
      seq {
        if ie.MoveNext()
        then 
          let nextValue, leftovers = f ie.Current acc
          if nextValue.IsSome then yield nextValue.Value
          yield! grouped_by_with_acc f leftovers ie
        else
          let rems = f2 acc
          if rems.IsSome then yield rems.Value
      }
    seq {
      yield! grouped_by_with_acc f [] (s.GetEnumerator())
    }    

Now we can specify a function to use for the leftovers.  This time we should get the right cows.

module TestGroupedByWithMatching =
  let Cows newValue acc =
    let newList = newValue :: acc
    match newList with
    | "cow" :: color :: "one" :: t -> Some [color], []
    | a :: b :: t -> None, [a; b]
    | l -> None, newList
  
  let IgnoreLeftovers f = None
  
  let someCows = seq { for i in ["one"; "red"; "cow"; "and"; "one"; "white"; "cow"; "and"; "three"; "green"; "cows"] -> i}
  let cowColors = someCows |> Seq.grouped_by_with_leftover_processing Cows IgnoreLeftovers
  printfn "cowColors are %A" cowColors
  
// This prints:  
// cowColors are seq [["red"]; ["white"]]

But we've got two separate functions (grouped_by_with_leftover_processing and grouped_by) that duplicate most of their code.  grouped_by is just grouped_by_with_leftover_processing so the final code is:

module Seq =  
  let grouped_by_with_leftover_processing f (f2: 'a list -> list<'a> option) (s: seq<'a>)=
    let rec grouped_by_with_acc (f: 'a -> 'a list -> 'a list option * 'a list) acc (ie: IEnumerator<'a>) =
      seq {
        if ie.MoveNext()
        then 
          let nextValue, leftovers = f ie.Current acc
          if nextValue.IsSome then yield nextValue.Value
          yield! grouped_by_with_acc f leftovers ie
        else
          let rems = f2 acc
          if rems.IsSome then yield rems.Value
      }
    seq {
      yield! grouped_by_with_acc f [] (s.GetEnumerator())
    }    

  let YieldReversedLeftovers (f: 'a list) = 
    if f.IsEmpty
    then None
    else Some (List.rev f)

  let grouped_by f s =
    grouped_by_with_leftover_processing f YieldReversedLeftovers s

  let group_by_length_n n s =
    let grouping_function newValue acc =
      let newList = newValue :: acc
      // If we have the right length, return
      // a Some as the first value.  That'll 
      // be yielded by the sequence.
      if List.length acc = n - 1
      then Some (List.rev newList), []
      // If we don't have the right length,
      // use None (so nothing will be yielded)
      else None, newList  
    grouped_by grouping_function s