2013-11-19 2 views
3

Может кто-то указать мне в направлении, почему моя система говорит мне, что у меня заканчивается память для приложений, когда я запускаю следующий код над текстом «большой» (37000 строк) файл?stringByAppendingString вызывает у системы нехватку памяти для приложений

-(void) writeToFile: (NSString*)filePath withSeparator:(NSString*) fieldSep{ 
NSString* completeFile = [[[NSString alloc] initWithString:@""] autorelease]; 
for(int i=0;i<[self numberOfRows];i++){ 
    printf("im at line number... %i of %i\n",i,[self numberOfRows]); 
    for(int j=0;j<[self numberOfColumns];j++){ 
     completeFile = [completeFile stringByAppendingString:[self objectInRow:i column:j]]; 

     if(j<[self numberOfColumns]-1){ 
      //separator for all columns except last one 
      completeFile = [completeFile stringByAppendingString:fieldSep]; 
     } 
    } 
     completeFile = [completeFile stringByAppendingString:@"\n"]; 
} 
NSError *error = nil; 
[completeFile writeToFile:filePath atomically:NO 
       encoding:NSStringEncodingConversionAllowLossy error:&error]; 
if(error){ 
    NSLog(@"Error writing file at %@\n%@", 
      filePath, [error localizedFailureReason]); 
} 

}

Я добавил Printf для отладки причины, первые 4000 строк, кажется, происходят мгновенно, затем медленно замедляется ... мой файл содержит более 37000 строк, подобные этим:

1893-11-6 136 194 165 
+2

Это пример [Schlemiel алгоритм художника] (http://en.wikipedia.org/wiki/Schlemiel_the_Painter's_algorithm) в действии. –

+0

Ну, не пример Спольского; 'NSString' не работает как' strcat'. :) –

+0

@StevenFisher: здесь копия делается каждый раз, когда добавляется строка, а не линейный поиск 0 в примере Stratsky's 'strcat'. Но принцип тот же - добавление O (n), поэтому построение строки - O (n^2). –

ответ

7

Когда вы выделяете объекты с использованием заводских методов, объекты добавляются в autoreleasepool. Автореферат удаляется только при прохождении цикла событий после того, как ваш IBAction вернулся.

Трюк здесь состоял бы в том, чтобы поместить содержимое вашей петли в свой собственный autorelasepool.

Но давайте сначала рассмотрим самую большую проблему. Существует класс NSMutableString, который вы должны использовать здесь, что резко сократило бы количество объектов, которые вам нужно создать.

Мы поменяемся completeFile к NSMutableString, построенная с использованием метода фабрики, а затем добавить к нему:

-(void) writeToFile: (NSString*)filePath withSeparator:(NSString*) fieldSep{ 
    NSMutableString* completeFile = [NSMutableString string]; 
    for(int i=0;i<[self numberOfRows];i++){ 
     printf("im at line number... %i of %i\n",i,[self numberOfRows]); 
     for(int j=0;j<[self numberOfColumns];j++){ 
      [completeFile appendString:[self objectInRow:i column:j]]; 

      if(j<[self numberOfColumns]-1){ 
       //separator for all columns except last one 
       completeFile appendString:fieldSep]; 
      } 
     } 
      [completeFile appendString:@"\n"]; 
    } 
    NSError *error = nil; 
    [completeFile writeToFile:filePath atomically:NO 
        encoding:NSStringEncodingConversionAllowLossy error:&error]; 
    if(error){ 
     NSLog(@"Error writing file at %@\n%@", 
       filePath, [error localizedFailureReason]); 
    } 
} 

Это оставляет еще одна проблема, хотя. Смотрите, что [self objectInRow:i column:j]? Это все еще (предположительно) автореализованный объект. Это не будет очищено.

Возможно, мы сделали свой код, выполняемый без сбоев, в зависимости от размера данных, но это вопрос о том, когда он выходит из строя не если.

Чтобы исправить это, нам необходимо ввести autoreleasepools. Давайте сделаем одну строку в строке и за столбец. Это может показаться чрезмерным (и, действительно, именно в этом случае, поскольку мы исключили использование autoreleasepool во внешнем цикле), но autoreleasepools довольно дешевы. Если вы делаете цикл над большим количеством данных, это просто хорошая практика.

Вы можете заменить каждый из ваших для блоков с @autorelease блоков, например:

for(int i=0;i<[self numberOfRows];i++){ 

С:

for(int i=0;i<[self numberOfRows];i++) @autoreleasepool { 

Это дает нам этот код:

-(void) writeToFile: (NSString*)filePath withSeparator:(NSString*) fieldSep{ 
    NSMutableString* completeFile = [NSMutableString string]; 
    for(int i=0;i<[self numberOfRows];i++) @autoreleasepool { 
     printf("im at line number... %i of %i\n",i,[self numberOfRows]); 
     for(int j=0;j<[self numberOfColumns];j++) @autoreleasepool { 
      [completeFile appendString:[self objectInRow:i column:j]]; 

      if(j<[self numberOfColumns]-1){ 
       //separator for all columns except last one 
       completeFile appendString:fieldSep]; 
      } 
     } 
      [completeFile appendString:@"\n"]; 
    } 
    NSError *error = nil; 
    [completeFile writeToFile:filePath atomically:NO 
        encoding:NSStringEncodingConversionAllowLossy error:&error]; 
    if(error){ 
     NSLog(@"Error writing file at %@\n%@", 
       filePath, [error localizedFailureReason]); 
    } 
} 

Заключительное обратите внимание. Ваша ошибка здесь небезопасна. Что происходит с указателем ошибки, переданным таким образом на Успех не определен.

[completeFile writeToFile:filePath atomically:NO 
        encoding:NSStringEncodingConversionAllowLossy error:&error]; 
    if(error){ 
     NSLog(@"Error writing file at %@\n%@", 
       filePath, [error localizedFailureReason]); 
    } 

Вместо этого, вы хотите:

BOOL ok = [completeFile writeToFile:filePath atomically:NO 
        encoding:NSStringEncodingConversionAllowLossy error:&error]; 
    if(!ok){ 
     NSLog(@"Error writing file at %@\n%@", 
       filePath, [error localizedFailureReason]); 
    } 

Это, то должен делать то, что вы хотите.

+1

ARC здесь не используется. См. Вызов 'autorelease'? –

+0

К сожалению, извините. Не заметил. Ну, ARC следует использовать. –

+2

Абсолютно. И я думаю, что ** NSMutableString ** также следует использовать. :) –

1

Чтобы добавить эти строки, вы должны использовать NSMutableString.

+1

Это действительно исправлено, я изменился на NSMutableString и использовал fullFile appendString вместо stringByAppendingString, и он делает все 37000 строк почти мгновенно. Спасибо всем за ответ! – Ockie

+0

Да. NSMutableString довольно эффективен. Я не знаю точно, что это алгоритм для изменения размера, но это, безусловно, умнее, чем перераспределение на каждом добавлении. :) –

4

Проблема в том, что каждый звонок stringByAppendingString: создает новый объект с автореализацией NSString. Однако, поскольку метод продолжается в цикле, autorelease не получает шанс освободить эти объекты.

Вы можете решить, добавив autorelease бассейн вокруг внутреннего контура, как это:

for(int i=0;i<[self numberOfRows];i++){ 
    printf("im at line number... %i of %i\n",i,[self numberOfRows]); 
    @autoreleasepool { 
     for(int j=0;j<[self numberOfColumns];j++){ 
      completeFile = [completeFile stringByAppendingString:[self objectInRow:i column:j]]; 

      if(j<[self numberOfColumns]-1){ 
       //separator for all columns except last one 
       completeFile = [completeFile stringByAppendingString:fieldSep]; 
      } 
     } 
    } 
     completeFile = [completeFile stringByAppendingString:@"\n"]; 
} 
+0

Спасибо за ваш ответ! – Ockie

Смежные вопросы