开发者

Objective C beginner question, casting & data initialization

I'm new to Objective C, translating existing code from Actionscript 2 for my first project. I've been trying to keep my data structures simple which for me means C-like arrays and strings rather than NS classes. I have a bunch of objects which contain a single byte char of data - they are scrabble tiles, types 'A' through 'Z'. I want to create an array of letter values in a way that the key/value pairs are easily readable and modifiable in code.

In AS2 it is simply this:

//init
letterScore = new Object({E:1, S:1, I:1, A:2, R:2 ... Z:10});

//assigned in 2dArray:
map[0]="WORD".split(); 
map[1]="GRID".split(); 
map[2]="HERE".split(); 
map[3]="!# @".split(); 

tile.letter=map[x][y];

//access
if(tile.letter>='A' && tile.letter<='Z'){
  wordScore+= letterScore[tile.letter];
}

In objective C, I have already defined and read in the map in a similar fashion, my tile.letter data type is char. Is there a way to get an associative array with such simple data types as char:int pairing? Can it be done in 1 line of code similar to the AS2 init line above? The books I'm reading suggest I have to wrap the values as NSNumber, and convert between char and NSString to access them, which seems unwieldy, but if I stick with C like arrays, I lose the readability of keys. In Java I could just extend the array class to handle the casting, but the books say that's a bad idea in Objective C, so I'm completely lost.

Edit: I think to avoid the constant boxing and unboxing that people are suggesting (am I using that term right?) and to retain readability, my best option is this dreadful hack...

#define ASCII_2_ARRAY -65
int letterScore[]={0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7开发者_JS百科, 8, 9, 0, 1, 2, 3, 4, 5}; 
//                 A  B  C  D  E  F  G  H  I  J  K  L  M  N  O  P  Q  R  S  T  U  V  W  X  Y  Z.

then access is simply

score += letterScore[tile.letter + ASCII_2_ARRAY];

Is there a downside to doing it this way?


You can create an NSDictionary easily, initialized with your data in a variety of ways. Typing a massive statement like

NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:
                              [NSNumber numberWithInt:1], @"A",
                              ...
                              [NSNumber numberWitHInt:10], @"Z",
                              nil];

would certainly work. Accessing this would be easy, to say, extract a letter's value:

int score = [[dict objectForKey:theLetter] intValue];

where theLetter is a letter from a word you are scoring.

Once you get used to the apparent "wordyness" of Objective-C, it's pretty cool what you can do with just a few basic types.


NSDictionary and NSArray, while annoyingly wordy, are the "simple data structures" in this neck of the woods. I'd suggest changing your tile.letters to NSStrings.

If you're going to use Obj-C and (I assume) Cocoa, you'll have to get used to NSDictionary and the fact that it can't handle non-object types. There's no problem having an NSString that contains only one character, as Mark has shown, and you can type all the literal NSStrings you want.

If it seems funny, think of a "character" as nothing more than a string of length 1. This is simply the inverse of the funny C idiom, where a "string" is nothing more than some number of concatenated characters.


In reply to your comment:

// Get a random letter from the alphabet
NSString *alphabet = [[NSString alloc] initWithString:@"ABCDEFGHIJKLMNOPQRSTUVWXYZ"];
NSString *letter = [alphabet substringWithRange:NSMakeRange(random() % ALPHABET_LENGTH, 1)];

/usr/share/dict/words is 2.5MB. That's around 10% of the memory footprint of the smallest Cocoa application I've got running right now (Preview, with no open documents). The three bits of compression saves you half a megabyte. Clicking a button uses about the same; moving the window around allocates more than that. Have you measured the performance of the NSString version?

// Create an NSString with one NSString character
NSString *justOneChar = [NSString stringWithString:@"A"];
// Get a char out of an NSString
char c;
// You can do this if you're sure that the character is ASCII
c = (char)[justOneChar characterAtIndex:0];

// Create an NSString using one char
char c = 'A';
unichar uc = c;
NSString * justOneOtherChar = [NSString stringWithCharacters:&uc length:1];
char oc = (char)[justOneOtherChar characterAtIndex:0];

C strings need to be nul-terminated, too.

You can also let Cocoa handle your English dictionary; see Dave DeLong's DDFileReader class to get the text file into NSStrings initially, then if you want, write them out and read them in using NSArray's writeToFile:atomically: and -initWithContentsOfFile:`


Since

  • You have a small array
  • The array size is fixed
  • Every element in the array can be represented by a primitive type
  • You perform arithmetic operations on the array elements

your problem is begging you to use a C array. Your idea of using

int letterScore[]={0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5}; 
//                 A  B  C  D  E  F  G  H  I  J  K  L  M  N  O  P  Q  R  S  T  U  V  W  X  Y  Z.

is good enough.

As you’ve noticed, the main issue is mapping characters to positions in the array. In general, you could use a preprocessor macro like:

#define CHAR2INDEX(x) (x - 'A')

score += letterScore[CHAR2INDEX(tile.letter)];

I agree that initialising the array is not quite like AS but, then again, C is a different language that tries to be as simple and efficient as possible. That said, there are alternative ways to initialise the array.

Firstly, you could use something like:

int letterScore[] = {
    ['A' - 'A'] = 0,
    ['B' - 'A'] = 1,
    ['C' - 'A'] = 2,
    …
};

or, using that previous macro,

int letterScore[] = {
    [CHAR2INDEX('A')] = 0,
    [CHAR2INDEX('B')] = 1,
    [CHAR2INDEX('C')] = 1,
    …
};

If wasting 65 ints is not a problem — which shouldn't be unless you're targeting a device with severe memory limitation — you could have an array whose 65 first elements are unused. That’d make things more readable. For instance:

int letterScore[] = { ['A'] = 0, ['B'] = 1, ['C'] = 2, … };

score += letterScore[tile.letter];
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜