2010-11-27 62 views
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);

有人可以幫我解決這個問題嗎? 謝謝! [游泳池釋放];

+2

我可以推薦一個功能更全面的CSV解析器嗎? https://github.com/davedelong/CHCSVParser – 2010-11-28 21:10:23

回答

0

您的tempFirstLine NSString對象是使用autorelease聲明的,並作爲您的NSString行返回,然後將其釋放。

嘗試使用這樣的:

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

它仍然無法正常工作。我不認爲這是由tempFirstLine引起的。 tempFirstLine是一個autorelease對象,但我分配了一個新的對象,它是*行來獲取字符串,所以如果我釋放*行對象,它不會影響tempFirstLine對象。 – 2010-11-27 12:58:03

2

不要使用-retainCount。

對象的絕對保留數是毫無意義的。

您應該致電release完全相同的次數導致對象被保留。沒有更少(除非你喜歡泄漏),當然,沒有更多(除非你喜歡崩潰)。

查看Memory Management Guidelines的全部細節。


有代碼中的幾個問題:

  • 你沒有按照正確的init模式。你應該在那裏有一個self = [super init...]; if (self) {...}

  • tempFileName是一個retain屬性,您將其分配給alloc/init的結果。它會被泄露。

  • 不可變的空字符串([[NSString alloc] init])幾乎沒有用處。實際上,stringFromFileAtPath正在被泄露(技術上 - 實施細節明智有一個空不變的單身字符串,因此,沒有真正的泄漏,但....仍然...)

  • 最後,崩潰:您的readLine方法正確地返回一個自動釋放對象。然而,消耗返回值readLinewhile()循環也會返回該值,導致雙重釋放並試圖釋放已釋放的值。

您應該「構建並分析」您的代碼。我敢打賭,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會使開發人員產生誤解。使用儀器。

+0

您好,非常感謝您的回覆。不幸的是,自動釋放的對象不是崩潰的根本原因。我將對象註釋掉並返回一個不是自動釋放對象的字符串對象,但它仍然崩潰。 我也嘗試構建和分析,但除了「構建成功」信息外,我什麼都沒有。 我發現崩潰是由stringFromFileAtPath對象引起的,如果我不在readLine函數的末尾釋放它,應用程序不會崩潰,但內存不斷增加。這個應用程序將處理一個相對較大的csv文件,所以這是不可接受的。 – 2010-11-28 08:22:00

0

Replac這樣的:

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

與此:

NSString *stringFromFileAtPath = nil; 

和擺脫[stringFromFileAtPath release]語句。

第一行創建一個指向您從不使用的新字符串對象的指針,因爲您會立即用指向其他字符串對象的指針覆蓋指針,因爲您不擁有它們/沒有創建它們。既然你正在釋放它們,你就會崩潰。

您與tempFirstLine發生同樣的錯誤。