我有一个函数,用于从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解析,那么App的main函数会在尝试释放自动释放池时给出双重自由错误。“ * __NSAutoreleaseFreedObject():释放先前取消分配的对象(0x6a26050)忽略“
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; int retVal = UIApplicationMain(argc,argv,nil,nil);
有人可以帮我解决这个问题吗? 谢谢! [池发布];
答案 0 :(得分:2)
对象的绝对保留计数毫无意义。
您应该调用release
与导致保留对象完全相同的次数。不会少(除非你喜欢泄漏),当然,没有更多(除非你喜欢崩溃)。
有关详细信息,请参阅Memory Management Guidelines。
您的代码中存在一些问题:
您没有遵循正确的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
具有误导性。使用仪器。
答案 1 :(得分:0)
您的tempFirstLine NSString对象是使用autorelease声明的,并作为NSString行返回,然后释放。
尝试使用:
while (!isFinalLine) {
NSString *line = [csvParser readLine];
if ([line isEqualToString:@"EOF"]) {
isFinalLine = TRUE;
}
NSLog(@"%@",line);
}
答案 2 :(得分:0)
重申:
NSString *stringFromFileAtPath = [[NSString alloc] init];
用这个:
NSString *stringFromFileAtPath = nil;
并删除[stringFromFileAtPath release]
语句。
第一行创建一个指向您永远不会使用的新字符串对象的指针,因为您会立即使用指向其他位置的字符串对象的指针覆盖指针,因为您不拥有它们而不需要释放它们/没有创造它们。因为你正在发布它们,所以你会崩溃。
您使用tempFirstLine
犯了同样的错误。