2010-09-10 5 views
6

Я написал эту функцию, которая перетасовки содержимого NSString, и, похоже, она работает, но время от времени она падает. Это может быть обходным путем, но я помещаю символы в массив, произвольно меняю элементы в массиве и затем возвращаю массив обратно в строку.Перемешивание писем в NSString в Objective-C

Я не уверен, что я делаю, что это небезопасно, что приводит к его сбою. Я думал, что это возможно, что я устанавливаю finalLettersString = result, но я также пробовал finalLettersString = [NSString stringWithString:result], и это также приводит к сбоям. Причина, по которой я запутался, заключается в том, что она не падает каждый раз. Я просто нажимаю кнопку тасования, и иногда он падает. В каких местах я должен смотреть?

/* 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; 
} 

ответ

8

Было бы лучше, чтобы скопировать содержимое строки во временный буфер типа unichar и перемешайте содержимое буфера, вместо того чтобы создавать множество мелких строк.

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; 

Несколько вещей, которые вы должны следить за:

  1. строки Unicode могут содержать составные символы и суррогатные пары. Перемешивание этих объектов, скорее всего, приведет к недопустимой строке. В то время как суррогатные пары встречаются редко, нетрудно найти, что символ é состоит из двух символов (базовая строчная буква e и сочетание острого акцента).

  2. Для больших строк это может вызвать проблемы с памятью, потому что вы в конечном итоге используете в 3 раза больше места, чем исходная строка (1 × для исходной строки, 2 × для используемого буфера и 3 × для новой строки , а затем обратно вниз до 2 ×, когда мы освободим буфер).

+0

Спасибо! Определенно намного лучшее решение. Одна из проблем на самом деле заключалась в том, что вы не можете использовать NSUIntegers, потому что - на 0 приводит к действительно большому числу, а не -1, так как без него. Я получил его для работы с использованием ints и сохранения. В этой ситуации, как вы думаете, предпочтительнее сохранить или выделить явно? – jkeesh

+0

@jkeesh: Хорошая мысль о целых числах без знака. Что касается выделения явно, это зависит от целевой платформы. Если вы ориентируетесь на iPhone, некоторые говорят, чтобы избежать пула автозапуска, когда вы можете это сделать элегантно, но для Mac OS X (т. Е. Систем с гораздо большим объемом памяти, чем iPhone) это не будет иметь заметной разницы. Лично я всегда выделяю явно, а не противопоставляю 'autorelease' с' keep', но результат тот же самый. – dreamlax

+0

В этот день и эпоху интернационализации вы не можете предположить, что один unichar отображает один символ. – JeremyP

11

Вариант кода @ dreamlax, который не использует массив символов. Не так эффективно. Но у него нет проблемы с 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]; 
}