Archive for January, 2009

Fast enumeration on 10.4

Thursday, January 29th, 2009

Tomorrow I’m showing some software I wrote to a client, and I don’t want to lug the external Harddrive on which Leopard is installed with me. The laptop still runs 10.4 for testing Find It! Keep It!

The software uses fast enumeration on arrays and sets. Building with the Mac OS 10.5 SDK and with 10.4 as deployment target worked, but running the code on 10.4 failed. The method countByEnumeratingWithState:objects:count: was missing. So I thought I’d add it — I stole its implementation from Cocotron, and added conditional posing for 10.4 systems:


// Stolen from Cocotron

#import <AppKit/AppKit.h>

@interface NSArrayEX : NSArray
@end


@implementation NSArrayEX

+ (void) load
{
  NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

  OSErr err;
  SInt32 systemVersion;
  if ((err = Gestalt(gestaltSystemVersion, &systemVersion)) == noErr)
    if ((systemVersion & 0xfff0) == 0×1040)
      [self poseAsClass:[NSArray class]];

  [pool release];
}


-(NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)length;
{
  int numObjects=MIN([self count] - state->extra[0], length);
  int i=state->extra[0];
  int j=0;
  state->itemsPtr=stackbuf;

  for(j=0; j<numObjects; j++, i++)
    state->itemsPtr[j]=[self objectAtIndex:i];

  state->extra[0]+=numObjects;

  state->mutationsPtr=(unsigned long *)self;

  return numObjects;
}

@end




@interface NSSetEX : NSSet
@end


@implementation NSSetEX

+ (void) load
{
  NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

  OSErr err;
  SInt32 systemVersion;
  if ((err = Gestalt(gestaltSystemVersion, &systemVersion)) == noErr)
    if ((systemVersion & 0xfff0) == 0×1040)
      [self poseAsClass:[NSSet class]];

  [pool release];
}



-(NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)length;
{
  int i;
  state->itemsPtr=stackbuf;
  
  state->mutationsPtr=(unsigned long*)self;
  if(!state->state)
    state->state=(unsigned long)[self objectEnumerator];

  id en=(id)state->state;
  
  for(i=0; i<length; i++)
  {
    state->itemsPtr[i]=[en nextObject];
    if(!state->itemsPtr[i])
      return i;
  }

  return i;
  
}

@end

It works surprisingly well. Unfortunately I then discovered that you can only create a 16 bit grey image context on 10.5 Oh well.

(Sorry for the horrible layout — I don’t have the time to dink with Wordpress right now)

setStringValue not clearing the screen ???

Tuesday, January 20th, 2009

I had this strange bug: Setting the string value of a NSTextField wasn’t removing its previous value before drawing the new one. The net result was a mess on the screen, and it was happening randomly.

Of course, I started by blaming Cocoa… I tried using setNeedsDisplay to no avail. Even marking the whole window as needing a refresh didn’t work.

So I stopped, and thought about the randomness. Seems a bit like a race condition… And then I realised my code was running in a sub-thread. Using performSelectorOnMainThread to update the UI once the thread had completed fixed the bug.