2010-09-10 16 views
6

He escrito esta función que mezcla el contenido de un NSString, y parece funcionar, pero de vez en cuando se cuelga. Esto puede ser una forma indirecta, pero pongo los caracteres en una matriz, intercambiamos los elementos en la matriz aleatoriamente, y luego volvemos a convertir la matriz en una cadena.Mezclar letras en un NSString en Objective-C

No estoy seguro de lo que estoy haciendo que no es seguro que hace que se bloquee. Pensé que posiblemente estaba configurando finalLettersString = result, pero también intenté finalLettersString = [NSString stringWithString:result] y eso también falla. La razón por la que estoy confundido es porque no se cuelga todo el tiempo. Sigo presionando el botón de reproducción aleatoria y, a veces, falla. ¿En cuales lugares debo mirar?

/* This function shuffles the letters in the string finalLettersString */ 

-(IBAction)shuffleLetters:(id)sender{ 
    int length = [finalLettersString length]; 
    NSMutableArray * letters = [NSMutableArray arrayWithCapacity:length]; 
    NSLog(@"final letters: %@", finalLettersString); 
    for(int i = 0; i < length; i++){ 
     char ch = [finalLettersString characterAtIndex:i]; 
     NSLog(@"%c", ch); 
     NSString * cur = [NSString stringWithFormat:@"%c", ch]; 
     [letters insertObject:cur atIndex:i]; 
    } 

    NSLog(@"LETTERS:: %@", letters); 

    for(int i = length - 1; i >= 0; i--){ 
     int j = arc4random() % (i + 1); 
     //NSLog(@"%d %d", i, j); 
     //swap at positions i and j 
     NSString * str_i = [letters objectAtIndex:i]; 
     [letters replaceObjectAtIndex:i withObject:[letters objectAtIndex:j]]; 
     [letters replaceObjectAtIndex:j withObject:str_i];  
    } 
    NSLog(@"NEW SHUFFLED LETTERS %@", letters); 

    NSString * result = @""; 
    for(int i = 0; i < length; i++){ 
     result = [result stringByAppendingString:[letters objectAtIndex:i]]; 
    } 

    NSLog(@"Final string: %@", result); 
    finalLettersString = result; 
    finalLetters.text = finalLettersString; 
} 

Respuesta

8

Sería mejor para copiar el contenido de la cadena en un buffer temporal del tipo unichar y mezclar el contenido de la memoria intermedia, en lugar de crear un montón de pequeñas cadenas.

NSUInteger length = [finalLettersString length]; 

if (!length) return; // nothing to shuffle  

unichar *buffer = calloc(length, sizeof (unichar)); 

[finalLettersString getCharacters:buffer range:NSMakeRange(0, length)]; 

for(int i = length - 1; i >= 0; i--){ 
    int j = arc4random() % (i + 1); 
    //NSLog(@"%d %d", i, j); 
    //swap at positions i and j 
    unichar c = buffer[i]; 
    buffer[i] = buffer[j]; 
    buffer[j] = c; 
} 

NSString *result = [NSString stringWithCharacters:buffer length:length]; 
free(buffer); 

// caution, autoreleased. Allocate explicitly above or retain below to 
// keep the string. 
finalLettersString = result; 

Un par de cosas que tendrá que tener en cuenta:

  1. cadenas Unicode pueden contener caracteres compuestos y los pares suplentes. Mezclar estos alrededor probablemente dará como resultado una cadena inválida. Mientras que los pares de sustitución son raros, no es raro encontrar que el carácter é se compone de dos caracteres (la letra minúscula base ey el acento agudo de combinación).

  2. Para cadenas grandes podría causar problemas de memoria porque termina usando 3 veces más espacio que la cadena original (1 × para la cadena original, 2 × para la memoria tampón que usamos y 3 × para la nueva cadena , y luego vuelva a bajar a 2 × una vez que liberamos el buffer).

+0

Gracias! Definitivamente una solución mucho mejor. Un problema en realidad fue que no puede usar NSUIntegers, porque - en 0 lleva a un número realmente grande y no a -1 ya que no está firmado. Lo tengo para trabajar usando Ints y retención. En esta situación, ¿cree que es preferible retener o asignar explícitamente? – jkeesh

+0

@jkeesh: Buen punto sobre los enteros sin signo. Con respecto a la asignación explícita, depende de la plataforma objetivo. Si tiene como objetivo el iPhone, algunos dicen que debe evitar el grupo de liberación automática cuando puede hacerlo elegantemente, pero para Mac OS X (es decir, sistemas con mucha más memoria que el iPhone) no habrá una diferencia notable. Personalmente, siempre asigno explícitamente en lugar de contrarrestar un 'autorelease' con un 'tener', pero el resultado es el mismo de cualquier manera. – dreamlax

+0

En esta época de internacionalización, no se puede asumir que unichar se asigna a un personaje. – JeremyP

11

Una variante del código de @ dreamlax que no utiliza char array. No tan eficiente seguro. Pero no tiene el problema Unicode.

NSMutableString *randomizedText = [NSMutableString stringWithString:currentText]; 

NSString *buffer; 
for (NSInteger i = randomizedText.length - 1, j; i >= 0; i--) 
{ 
    j = arc4random() % (i + 1); 

    buffer = [randomizedText substringWithRange:NSMakeRange(i, 1)]; 
    [randomizedText replaceCharactersInRange:NSMakeRange(i, 1) withString:[randomizedText substringWithRange:NSMakeRange(j, 1)]]; 
    [randomizedText replaceCharactersInRange:NSMakeRange(j, 1) withString:buffer]; 
}