выпуск ранее освобожденной проблемы объекта - PullRequest
0 голосов
/ 27 ноября 2010

У меня есть функция, которая использует для чтения одной строки из файла CSV. Но я получил освобождение ранее освобожденной ошибки объекта, или иногда это ошибка «двойного освобождения».

Я пытаюсь отследить, какой объект вызывает эту ошибку, по адресу памяти ошибок, но мне не удалось это сделать.

Вот код:

    @interface CSVParser : NSObject {
    NSString *fileName;
    NSString *filePath;
    NSString *tempFileName;
    NSString *tempFilePath;

    //ReadLine control
    BOOL isFirstTimeLoadFile;
    NSString *remainContent;
}

@property(nonatomic,retain) NSString *fileName;
@property(nonatomic,retain) NSString *filePath;
@property(nonatomic,retain) NSString *tempFileName;
@property(nonatomic,retain) NSString *tempFilePath;

@property(nonatomic,retain) NSString *remainContent;

-(id)initWithFileName:(NSString*)filename;

-(BOOL)checkAndCopyFile:(NSString *)filename;
-(BOOL)checkAndDeleteTempFile;
-(NSString*)readLine;
-(NSArray*)breakLine:(NSString*)line;

@end

@implementation CSVParser

@synthesize fileName;
@synthesize filePath;
@synthesize tempFileName;
@synthesize tempFilePath;

@synthesize remainContent;

-(id)initWithFileName:(NSString *)filename{
    //ReadLine control
    isFirstTimeLoadFile = TRUE;

    self.fileName = filename;
    self.tempFileName = [[NSString alloc] initWithFormat:@"temp_%@",fileName];
    NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentDir = [documentPaths objectAtIndex:0];
    self.filePath = [documentDir stringByAppendingPathComponent:fileName];
    self.tempFilePath = [documentDir stringByAppendingPathComponent:tempFileName];
    if ([self checkAndCopyFile:fileName]) {
        return self;
    }else {
        return @"Init Failure";
    }

}

-(BOOL)checkAndCopyFile:(NSString *)filename{
    BOOL isFileExist;
    NSError *error = nil;
    NSFileManager *fileManger = [NSFileManager defaultManager];
    isFileExist = [fileManger fileExistsAtPath:filePath];
    if (isFileExist) {
        //Create a temp file for reading the line.
        [fileManger copyItemAtPath:filePath toPath:tempFilePath error:&error];
        return TRUE;
    }else {
        return FALSE;
    }
}

-(NSString*)readLine{
    NSError *error = nil;
    //Read the csv file and save it as a string
    NSString *tempFirstLine = [[[NSString alloc] init] autorelease];
    NSString *stringFromFileAtPath = [[NSString alloc] init];
    if (isFirstTimeLoadFile) {
        NSLog(@"Into First Time");
        stringFromFileAtPath = [NSString stringWithContentsOfFile:tempFilePath 
                                                         encoding:NSUTF8StringEncoding 
                                                            error:&error];
        isFirstTimeLoadFile = FALSE;
    }else {
        NSLog(@"Not First Time");
        NSLog(@"Not First Time count:%d",[remainContent retainCount]);
        stringFromFileAtPath = remainContent;
        remainContent = nil;
    }
    if ([stringFromFileAtPath isEqualToString:@""]) {
        [stringFromFileAtPath release];
        return @"EOF";
    }

    //Get the first line's range
    NSRange firstLineRange = [stringFromFileAtPath rangeOfString:@"\n"];
    //Create a new range for deletion. This range's lenght is bigger than the first line by 1.(Including the \n)
    NSRange firstLineChangeLineIncludedRange;
    if (stringFromFileAtPath.length > 0 && firstLineRange.length == 0) {
        //This is the final line.
        firstLineRange.length = stringFromFileAtPath.length;
        firstLineRange.location = 0;
        firstLineChangeLineIncludedRange = firstLineRange;
    }else {
        firstLineRange.length = firstLineRange.location;
        firstLineRange.location = 0;
        firstLineChangeLineIncludedRange.location = firstLineRange.location;
        firstLineChangeLineIncludedRange.length = firstLineRange.length + 1;
    }
    //Get the first line's content
    tempFirstLine = [stringFromFileAtPath substringWithRange:firstLineRange];
    remainContent = [stringFromFileAtPath stringByReplacingCharactersInRange:firstLineChangeLineIncludedRange withString:@""];

    [stringFromFileAtPath release];
    error = nil;
    return tempFirstLine;
}

И следующий код показывает, как я использую класс выше:

CSVParser *csvParser = [[CSVParser alloc] initWithFileName:@"test.csv"];
BOOL isFinalLine = FALSE;

while (!isFinalLine) {
    NSString *line = [[NSString alloc] init];
    line = [csvParser readLine];
    if ([line isEqualToString:@"EOF"]) {
        isFinalLine = TRUE;
    }
    NSLog(@"%@",line);
    [line release];
}
[csvParser release];

Если я выполню код и закончу синтаксический анализ csv, основная функция приложения выдаст мне ошибку двойного освобождения при попытке освободить пул автоматического выпуска. "* __NSAutoreleaseFreedObject (): выпуск ранее освобожденный объект (0x6a26050) игнорируется "

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; int retVal = UIApplicationMain (argc, argv, nil, nil);

Может ли кто-нибудь помочь мне решить эту проблему? Спасибо! [релиз пула];

Ответы [ 3 ]

2 голосов
/ 27 ноября 2010

Не использовать -retainCount.

Абсолютное количество сохраняемых объектов не имеет смысла.

Вы должны вызывать release ровно столько раз, сколько вы вызывали сохранение объекта. Не меньше (если вам не нравятся утечки) и, конечно же, не больше (если вам не нравятся сбои).

Подробнее см. Рекомендации по управлению памятью .

<Ч />

В вашем коде есть несколько проблем:

  • вы не следуете правильному шаблону init. У вас должно быть где-то self = [super init...]; if (self) {...}.

  • tempFileName - это свойство retain, и вы присваиваете ему результат alloc/init. Будет утечка.

  • Неизменяемая пустая строка ([[NSString alloc] init]) почти никогда не используется. И, на самом деле, stringFromFileAtPath просачивается (технически - в деталях реализации есть пустая неизменяемая одиночная строка и, таким образом, нет реальной утечки, но .... все же ...)

  • Наконец, сбой: ваш метод readLine правильно возвращает автоматически выпущенный объект. Тем не менее, ваш while() цикл, потребляющий возвращаемое значение readLine, также release возвращает это возвращаемое значение, что приводит к двойному освобождению и попытке освободить то, что уже было освобождено.

Вы должны «построить и проанализировать» свой код. Могу поспорить, что статический анализатор llvm определит большинство, если не все, проблемы, о которых я говорил выше (и, возможно, еще некоторые, которые я пропустил).

<Ч />

При сборке с помощью анализатора у вас в окне «Сборка» выбраны «все сообщения» или «только проблемы с анализатором»? Потому что, глядя на код, я удивляюсь, что анализатор не уловил очевидную проблему с stringFromFileAtPath.

За исключением кода, у вас есть следующие строки, которые манипулируют stringFromFileAtPath:

NSString *stringFromFileAtPath = [[NSString alloc] init];
....
stringFromFileAtPath = [NSString stringWithContentsOfFile:tempFilePath 
                                                 encoding:NSUTF8StringEncoding 
                                                     error:&error];
....
stringFromFileAtPath = remainContent;
....
[stringFromFileAtPath release];

А remainContent устанавливается:

remainContent = [stringFromFileAtPath stringByReplacingCharactersInRange:firstLineChangeLineIncludedRange
                                                              withString:@""];

Вы освобождаете автоматически освобожденный объект. К память продолжает расти , как вы ее измеряете? Не используйте Activity Monitor, поскольку он почти бесполезен для разработчиков, поскольку retainCount вводит в заблуждение. Используйте инструменты.

0 голосов
/ 27 ноября 2010

Замените это:

NSString *stringFromFileAtPath = [[NSString alloc] init];

с этим:

NSString *stringFromFileAtPath = nil;

и избавьтесь от [stringFromFileAtPath release] операторов.

Первая строка создает указатель на новый строковый объект, который вы никогда не используете, потому что вы немедленно перезаписываете указатель указателем на строковые объекты из другого места, которые вам не нужно освобождать, потому что вы не являетесь их владельцем не создал их. Так как вы отпускаете их, вы получаете сбой.

Вы делаете ту же ошибку с tempFirstLine.

0 голосов
/ 27 ноября 2010

Ваш объект NSString tempFirstLine объявлен с автоматическим выпуском и возвращен как ваша строка NSString, которая затем освобождается.

Попробуйте использовать это:

while (!isFinalLine) {
NSString *line = [csvParser readLine];
if ([line isEqualToString:@"EOF"]) {
    isFinalLine = TRUE;
}
NSLog(@"%@",line);
}
...