0

我有一个函数用于从 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);

有人可以帮我解决这个问题吗?谢谢![池释放];

4

3 回答 3

2

不要使用 -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:@""];

您正在释放一个自动释放的对象。通过记忆不断上升,你是如何衡量它的?不要使用活动监视器,因为它对开发人员来说几乎和retainCount误导一样无用。使用仪器。

于 2010-11-27T19:14:03.113 回答
0

替换这个:

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

有了这个:

NSString *stringFromFileAtPath = nil;

并摆脱[stringFromFileAtPath release]陈述。

第一行创建了一个指向您从未使用过的新字符串对象的指针,因为您立即用指向其他地方的字符串对象的指针覆盖该指针,您不需要释放它们,因为您不拥有它们/没有创建它们。既然你要释放它们,你就会崩溃。

你犯了同样的错误tempFirstLine

于 2010-11-27T19:26:01.680 回答
0

您的 tempFirstLine NSString 对象使用 autorelease 声明,并作为您的 NSString 行返回,然后被释放。

尝试使用这个:

while (!isFinalLine) {
NSString *line = [csvParser readLine];
if ([line isEqualToString:@"EOF"]) {
    isFinalLine = TRUE;
}
NSLog(@"%@",line);
}
于 2010-11-27T12:47:01.030 回答