Uli's Web Site
[ Zathras.de - Uli's Web Site ]
Other Sites: Stories
Abi 2000
Stargate: Resurgence
Lost? Site Map!
     home | blog | moose | programming | articles >> blog

 Blog Topics

15 Most Recent [RSS]

 Less work through Xcode and shell scripts
2011-12-16 @600
 iTunesCantComplain released
2011-10-28 @954
 Dennis Ritchie deceased
2011-10-13 @359
 Thank you, Steve.
2011-10-06 @374
 Cocoa Text System everywhere...
2011-03-27 @788
 Blog migration
2011-01-29 @520
 All you need to know about the Mac keyboard
2010-08-09 @488
 Review: Sherlock
2010-07-31 @978
 Playing with Objective C on Debian
2010-05-08 @456
 Fruit vs. Obst
2010-05-08 @439
 Mixed-language ambiguity
2010-04-15 @994
 Uli's 12:07 AM Law
2010-04-12 @881
 Uli's 1:24 AM Law
2010-04-12 @874
 Uli's 6:28 AM Law
2010-04-12 @869
 Uli's 3:57 PM Law
2010-04-12 @867


Look-up tables for fun and profit

No time for blogging, so I thought I'd re-post a short programming trick I use a lot that I mentioned in a forum a while ago. This is a fairly fundamental trick, but it works slightly differently in many languages, so even if you know it already, you might appreciate seeing what Cocoa classes are best suited for this.

Look-up tables and selectors

Imagine you're writing your own programming language, and you want to let the user use WebView to display a browser in a window. All the IBAction functions on a web view are of the same form: -(void) methodName: (id)sender where the sender is generally ignored. So you can take the strings your programming language gets from the user and use NSSelectorFromString() to get a SEL, (the data type that @selector() returns) and then you can use [myWebView performSelector: theSel withObject: nil] or whatever to actually make the call. That way, you only need to write code for each kind of command, not for each and every command individually.

Of course, since Objective C is case-sensitive, and your programming language might be case-insensitive, you may want to use an NSDictionary to look up the right spelling for the commands. I.e.:

NSDictionary *possibleCommands = [NSDictionary dictionaryWithObjectsAndKeys:
        @"goBack:", @"goback",
        @"reload:", @"reload",
NSString* lowercasedCommand = [myParameter lowerString];
NSString* caseCorrectedCommand = [possibleCommands objectForKey: lowercasedCommand];
if( caseCorrectedCommand == nil ) // unknown command passed to XCMD.
   return; // TODO: Emit error message.
SEL theCommand = NSSelectorFromString(caseCorrectedCommand);
if( theCommand == nil ) // No selector of that name? Shouldn't be possible, except if our dictionary has a typo.
   return; // TODO: Emit error message.
[myWebView performSelector: theCommand withObject: nil];

To add a new command, all you have to do is add two simple list items to the dictionary declaration:

@"makeTextLarger:", @"maketextlarger",
for example.

One trick in the code above is the lowerString thing: NSDictionary is case-sensitive, too, so what I do is simply specify all the keys in the dictionary in lowercase. When I get a string from the user of my programming language, I turn it into lowercase, which ensures that no matter whether the user wrote "GOBACK", "GoBack", "goBack" or "goback", I always get the dictionary entry with the key "goback" (in this case "goBack:").

Another advantage of the dictionary approach is that you verify that whatever string is passed in is one that you actually support. So if the user accidentally specifies "release" (e.g. passes in the wrong variable), your code won't end up prematurely releasing your myWebView object and cause the whole app to crash. It's a security advantage.

Look-up tables and numbers

The same dictionary approach works for other stuff, too. A dictionary can contain any kind of object, so if you wrap a number in an NSNumber, you can have a dictionary that maps strings to numbers, and even numeric constants. A sample dictionary would look like:

NSDictionary *possibleInterpolations = [NSDictionary dictionaryWithObjectsAndKeys:
        [NSNumber numberWithInt: NSImageInterpolationDefault], @"default",
        [NSNumber numberWithInt: NSImageInterpolationNone], @"none",
        [NSNumber numberWithInt: NSImageInterpolationLow], @"low",
        [NSNumber numberWithInt: NSImageInterpolationHigh], @"high",
NSNumber* interpolationNumObject = [possibleInterpolations objectForKey: [myParameter lowerString]];
int myInterpolationNumber = NSImageInterpolationDefault;
if( const )
    myInterpolationNumber = [interpolationNumObject intValue];
// use myInterpolationNumber here...

Such a dictionary is what is referred to in computer science texts as a "look-up table".

Singleton for more performance

If you have a look-up table that you use a lot, you can also keep it around between method calls. E.g.:

-(void)  myLookupTablePoweredMagic
    static NSDictionary* sLocallyAccessibleGlobal = nil;
    if( sLocallyAccessibleGlobal == nil )
        sLocallyAccessibleGlobal = [[NSDictionary alloc] initWithObjectsAndKeys: ..., nil]; // singleton, intentional leak.
    // use it here.

Note that we use alloc/init here, because sLocallyAccessibleGlobal is effectively a global variable (that's what static means in this context), and will maintain its value the second time myLookupTablePoweredMagic is called (hence the == nil test). Since the only one who can work with sLocallyAccessibleGlobal is the myLookupTablePoweredMagic method, this is also a leak, because nobody ever releases the object we created. However, this is an intentional, one-time leak in this case. If this method is really used very frequently, we want the dictionary to be kept around while our application is running. And when our application is quit, the memory will be cleaned up anyway, so why bother explicitly releasing it?

Reader Comments: (RSS Feed)
Jean-Daniel writes:
"SEL theCommand = NSSelectorFromString(caseCorrectedCommand); if( theCommand == nil )" I don't think it's possible for NSSelectorFromString to return nil, even with a typo in your dictionary (except maybe if an objc runtime error occured). Whatever the string is, the runtime should create a new selector, or return an existing one. This is what the NSSelectorFromString ref says: Discussion To make a selector, NSSelectorFromString passes a UTF-8 encoded character representation of aSelectorName to sel_registerName and returns the value returned by that function. Note, therefore, that if the selector does not exist it is registered and the newly-registered selector is returned.
Comment on this article:
E-Mail: (not shown, hashed for Gravatar)
Web Site URL: (optional)
Comment: (plain text only)
Please Enter the following word:
Or E-Mail Uli privately.

Created: 2009-01-23 @323 Last change: 2014-04-24 @819 | Home | Admin | Edit
© Copyright 2003-2014 by M. Uli Kusterer, all rights reserved.