objective-c - 释放先前释放的对象问题

标签 objective-c cocoa memory-management autorelease

我有一个函数,用于从 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 解析,应用程序的 main 函数在尝试释放自动释放池时将给出双重释放错误。"* __NSAutoreleaseFreedObject(): release of先前释放的对象(0x6a26050)被忽略”

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

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

最佳答案

不要使用-retainCount。

对象的绝对保留计数是没有意义的。

您应该调用release与您导致对象被保留的次数完全相同。不能少(除非您喜欢泄漏),当然也不能多(除非您喜欢崩溃)。

请参阅Memory Management Guidelines了解完整详细信息。

<小时/>

您的代码中存在一些问题:

  • 您没有遵循正确的 init图案。你应该有一个self = [super init...]; if (self) {...}在那儿的某个地方。

  • tempFileNameretain属性,并将其分配为 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 一样无用。具有误导性。使用仪器。

关于objective-c - 释放先前释放的对象问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4291681/

相关文章:

ios - 使用两个持久存储协调器进行高效后台更新的陷阱

objective-c - View Controller 不会被推送

iphone - 我们可以在最后更改 iphone 项目的 bundle 显示名称吗?

objective-c - 快速发布

cocoa - 弧: How do you release a WindowController when the user closes the Window?

memory-management - 动态堆分配困惑

C: 使用 realloc 的内存错误

ios - 将多分钟数字(超过 2)分钟转换为 NSDate

ios - 为什么 NSURL 在方案之后解析带有双正斜杠的字符串与带有单个正斜杠的字符串不同?

c++ - Lambda 捕获和内存管理