Vincent Gable’s Blog

August 31, 2008

Better List Termination

Filed under: MacOSX,Objective-C,Programming,Usability | , ,
― Vincent Gable on August 31, 2008

As I’ve written before, using nil to terminate a variable-length list (eg [NSArray arrayWithObject:foo,bar,nil]) is a bad choice because of how easy it is to get a nil value in Objective-C without knowing it. (Unlike, say C++, doing stuff with nil won’t cause an exception, leading to some subtle bugs). Well, here is a solution.

A Better Sentinel

#define END_OF_LIST ((id) -1)
is my current choice of a good sentinel-value. It is an invalid pointer, so unlike nil, you should never get it back from some library. Sending it any message will fail fast, and recognizably (you will see 0xFF...FF as an illegal address somewhere in the back-trace).

Example:


id aNilObject = nil;
NSArray *standard = [NSArray arrayWithObjects:@"1", @"2", aNilObject, @"4", nil];
LOG_ID(standard);
NSArray *better = [NSArray arrayWithSentinelTerminatedObjects:@"1", @"2", aNilObject, @"4", END_OF_LIST];
LOG_ID(better);


standard = (
   1,
   2
)
better = (
   1,
   2,
   <null>,
   4
)

The Code:


#define END_OF_LIST ((id) -1)

@implementation NSArray (SentinelTerminatedConstructors)

+ (NSArray*) arrayWithSentinelTerminatedObjects:(id) firstObject, ...
{
   NSMutableArray *array = [NSMutableArray array];
   
   if (firstObject != END_OF_LIST)
   {
      if(!firstObject)
         firstObject = [NSNull null];

      [array addObject:firstObject];
      
      id eachObject;
      va_list argumentList;
      va_start(argumentList, firstObject);
      while (END_OF_LIST != (eachObject = va_arg(argumentList, id)))
      {
         if(!eachObject)
            eachObject = [NSNull null];

         [array addObject:eachObject];
      }
      va_end(argumentList);
   }
   
   return array;
}

@end

Some Downsides

arrayWithSentinelTerminatedObjects” is too long of a name. If you’ve got a better one, please share!

You won’t get a compiler-warning if you leave out END_OF_LIST, but if you compile with -Wformat, you will get a warning if you leave out nil. The full story

There are six collection creation methods in Foundation which require nil termination to function properly. They are:

+[NSArray arrayWithObjects:]
-[NSArray initWithObject:]
+[NSDictionary dictionaryWithObjectsAndKeys:]
-[NSDictionary initWithObjectsAndKeys:]
+[NSSet setWithObjects:]
-[NSSet initWithObjects:]

These methods have been decorated with the NS_REQUIRES_NIL_TERMINATION macro, which adds an additional check to invocations of those methods to make sure the nil has been included at the end of the argument list. This warning is only available when compiling with -Wformat.

What do you think?

Frankly, I haven’t used END_OF_LIST-terminated collection creation enough yet to have a final verdict. So please let me know what you think! Was this code useful? Did it cause problems, or preempt them? As always, if you have a better solution, please share!

August 28, 2008

Lying With Pictures

Filed under: Design,Security | , , ,
― Vincent Gable on August 28, 2008

A fantastic discussion of lying with photography. One of the take-aways for me were that captioning a picture can be more effective then photoshopping it, especially since the text and image are processed separately in the brain (at least as far as we know).

For historical reference, here’s an article on staged photographs in early 1930’s advertising.

August 23, 2008

Don’t Buy Down Pillows

Filed under: Announcement,Usability | , ,
― Vincent Gable on August 23, 2008

After I saw duckling’s jumping out of trees (from Planet Earth), I had to try some down pillows. And they are great, except that the tips of the feathers eventually work their way out of the pillowcase and poke you. The pokes are so disruptive that they ruin sleep more then a slightly-less-comfortable pillow. I’m sure there’s a good metaphor in there somewhere.

August 22, 2008

Is an Application Running?

Filed under: Cocoa,MacOSX,Objective-C,Programming,Sample Code,UNIX | ,
― Vincent Gable on August 22, 2008

UPDATED 2009-01-13 to correctly detect background applications.

Pass in the bundle-identfier of an application, and this will tell you if it’s currently running or not:
- (BOOL) ThereIsARunningApplicationWithBundleID:(NSString*)bundleID; {
   ProcessSerialNumber PSN = {kNoProcess};
   while(GetNextProcess(&PSN) == noErr) {
      CFDictionaryRef info = ProcessInformationCopyDictionary(&PSN,kProcessDictionaryIncludeAllInformationMask);
      if(info) {
         NSString *theirBundleID = (NSString*)CFDictionaryGetValue(info, kIOBundleIdentifierKey);
         BOOL bundleIDMatches = [bundleID isEqualTo:(NSString*)theirBundleID];
         CFRelease(info);
         if(bundleIDMatches)
            return YES;
      }
   }
   return NO;
}

(It’s a good idea to test if an application is running before sending it an AppleEvent, because that will launch it.)

Interestingly, none of the the Process Manager APIs can see into another user’s process-space; but ps can; at least on OS X 10.5 and 10.4.

August 21, 2008

Are Line Numbers Just Clutter?

Filed under: Design,Programming,Usability | ,
― Vincent Gable on August 21, 2008

I think numbering every line in a source-code editor is (very mildly) harmful. It adds visual clutter; and that’s a bad thing.

Often you need to find a particular line number, say to locate a failed assert() — but every programming editor has a “Go to line#” command, which is faster to use.

Showing the line number the cursor is on is worthwhile, because almost no display space is lost.

If you have your left-hand column set to show you line numbers, try turning it off and see what happens. You may be able to focus on your code ever so slightly better.

August 20, 2008

Hardwired Strings

With every programming language/development environment I know of, you have to do extra work to make a string localizable. For example,
errorMessage = NSLocalizedString(@"This is hard!",@"");
not simply,
errorMessage = @"This is hard!";

And it’s not just extra work, it’s a ridiculous amount of work. E.g. to create an Objective-C string, you just put an @ in front of a standard C string. But to create a localized string, you have to surround your string in ()‘s, and prefix it with a seventeen character mixed-case function-name, and finally add an empty “note” for the translator. (Rarely, you may want to add a note for the translator, and it’s good that you can, but you should not be forced to write something when it is not necessary.)

There isn’t really an excuse for this; it’s just how languages have lazily evolved.

Non-localizable (“hardcoded”) strings are a huge issue, even though they are trivially simple for programmers to fix. The problem is that translators do not work with source code, they work with resource files. So when they find a “hardwired” string that must be changed in code, they tell a programmer to fix it. And that communication is slow and costly. Especially when you consider that good localizers are probably native to the place they are localizing for — meaning that having a real-time conversation involves at least one party staying up past 3AM.

I dream of a day where "this would be localized by default"; "and"("syntactic sugar for adding optional translator notes would be there too"); and creating C"literal C-strings" would be easy.

“Unicode support libraries” are doomed to institutionalized-failure, because they will never make it easier to do the right thing. It must be so easy to create a localizable program that you wouldn’t dream of doing anything else.

Localizing In Xcode

Let me say this again in slow motion: NEVER type in ANY English string without typing NSLocalizedString() around it! This will save you SO MUCH HASSLE later on when your app is popular. Remember that enterprising polyglots can localize your code from just the binary you ship if you follow a few rules of localization, so you may wake up one day and find that someone from across the world has mailed you a your app in another language. It’s a fuzzy feeling and it gets you instant market-share.

Wil Shipley

August 17, 2008

launchctl Gotcha

Filed under: Bug Bite,MacOSX,Programming,UNIX | , ,
― Vincent Gable on August 17, 2008

On Mac OS X 10.5, the launchd you talk to as non-root user is not the same launchd you talk to when root. So
$ launchctl list
$ sudo launchctl list
print out different lists, and the unprivileged `launchctl list` contains processes that the root-`launchctl list` does not know about. So running a launctl command as root may cause it to fail.

I have never seen another UNIX-ish tool that would fail when run as root, but succeed otherwise.

B-Movie Boxes

Filed under: Design,Programming,Quotes | , ,
― Vincent Gable on August 17, 2008

Computer interfaces still look like they came from the set of a 50s Science Fiction TV series. Polished metal, shiny everything. We’re fast approaching the ability in computer graphics to create any kind of space we want. Would that be a shiny shuttle interior or would it be a spacious, sunlit, gentle atrium view?

Keith Lang

I can’t recommend the book Emotional Design enough.

August 15, 2008

Members Are The Devil

Filed under: Bug Bite,Design,Programming
― Vincent Gable on August 15, 2008

More and more, I am of the opinion that a member-variable is a terrible thing to have in a class. Put more sanely, a class should have as few member variables as possible.

I have squashed too many bugs in the past couple of months, where two objects had their own fields that represented the same thing .. but then got out of synch. Only one object should ever store the same value.

I guess you could say this is database-normalization, applied to general programming. And I think in that space it makes more sense.

A corollary is, never cache a value that can be calculated, without a measured performance reason.

Older Posts »

Powered by WordPress