nickthedude
nickthedude

Reputation: 4973

iPhone OS: making a switch statement that uses string literals as comparators instead of integers

So I'd like to do this:

switch (keyPath) {
    case @"refreshCount":
        //do stuff
    case @"timesLaunched":
       //do other stuff
}

but apparently you can only use integers as the switch quantity. Is the only way to do this parse the string into an integer identifier and then run the switch statement?

like this:

nsinteger num = nil;

if (keyPath isEqual:@"refreshCount") {

num = 0

}

if (keyPath isEqual:@"timesLaunched") {

num = 1

}

I'm trying to optimize this code to be as quick as possible because its going to get called quite often.

thanks,

Nick

NOTE: Yes I'm using KVO so I am recieving a string in the "callback."

NOTE #2: So what made me first consider the switch statement is my original code implementation was like this:

if ([keyPath isEqual:@"refreshCount"] && ([[change valueForKey:@"new"] intValue] == 10)) { //  
NSLog(@"achievemnt hit inside");
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"title" message:@"Achievement Unlocked!" delegate:self cancelButtonTitle:@"cancel" otherButtonTitles:nil];
    [alert show];

I want to do a bunch of these with different XX values all in the same method:

if ([keyPath isEqual:@"refreshCount"] && ([[change valueForKey:@"new"] intValue] == 10)) {

//unlock small achievement

}
 if ([keyPath isEqual:@"refreshCount"] && ([[change valueForKey:@"new"] intValue] == 50))   {

//unlock bigger achievement

}   

//etc

This just seemed very inefficient to me but maybe I'm wrong.

Upvotes: 2

Views: 12653

Answers (3)

drawnonward
drawnonward

Reputation: 53689

The short answer is to not use strings in the first place. Barring that, you can have the strings be keys in a dictionary with integer (NSNumber) values. Or you can use the hashes of the strings.

switch ( [keyPath myQuickHash] ) {
case kHashRefreshCount:
case kHashTimesLaunched:
}

If there are just a few distinct strings, you can use the first (or last) 4 characters as a string literal and consider that the hash.

switch ( [keyPath stringLiteral] ) {
case 'refr':
case 'time':
}

Edit:

A switch statement is essentially a sparse array of code snippets. A hash table is a means of looking up an index in a sparse array given an arbitrary value. Given a known set of string inputs, a switch statement can operate like a hash table, which is to say, have constant lookup time. All you have to do is choose a hash algorithm with no collisions for the known inputs. If the set of inputs is not known, this is not an option, but in this question it is possible that all inputs are known.

This has absolutely nothing to do with how Apple implements their hash algorithm because you have to implement your own hash algorithm. The algorithm chosen could probably be as simple as adding up the length and letters in the string.

Upvotes: 2

bbum
bbum

Reputation: 162722

That is, for a cast statement to work, it would just have to call isEqualToString: anyway and would be just as slow, but probably not anywhere near as slow as you think.

The first question, of course, is have you measured performance and do you have evidence that the code is causing a performance issue?

If not, go and finish your app. A shipping app always outperforms an app still in development!

I would bet you don't have a performance issue; if all of your strings really are inline string constants -- @"refreshCount" and the like -- related to key-value observing, then it is quite likely that all of them are constant strings compiled into the app and, thus, comparison will be really fast because every time you mention "@refreshCount" it really is the same string at the same address (which compares very fast).

If you do have a quantifiable performance issue, post the details in a different question and someone can answer specifically. As it is, there isn't enough architectural or quantitative information to do anything more than speculate.

Upvotes: 6

jessecurry
jessecurry

Reputation: 22116

Why not just use an enum?

typedef enum _KeyPath
{
     KeyPathNone,
     KeyPathRefreshCount,
     KeyPathTimesLaunched,
     KeyPathCount
} KeyPath;

If you must use strings that you should be comparing with isEqualToString:

From the NSString docs:

Special Considerations
When you know both objects are strings, this method is a faster way to check equality than isEqual:.

Upvotes: 4

Related Questions