0

csv ファイルから 1 行を読み取る関数があります。しかし、以前に割り当て解除されたオブジェクトのエラーのリリース、または「二重解放」エラーが発生することがあります。

エラーメモリアドレスに基づいて、このエラーの原因となったオブジェクトを追跡しようとしましたが、これを行うことができませんでした。

コードは次のとおりです。

    @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 の解析を終了すると、自動解放プールを解放しようとすると、アプリのメイン関数で二重解放エラーが発生します

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

誰かがこの問題を解決するのを手伝ってくれますか? ありがとうございました![プール解放];

4

3 に答える 3

2

-retainCount は使用しないでください。

オブジェクトの絶対保持カウントは無意味です。

releaseオブジェクトを保持させたのとまったく同じ回数呼び出す必要があります。それ以下ではなく (リークが好きでない限り)、もちろんそれ以上でもありません (クラッシュが好きでない限り)。

詳細については、メモリ管理ガイドラインを参照してください。


コードにはいくつかの問題があります。

  • あなたは正しいinitパターンに従っていません。self = [super init...]; if (self) {...}どこかに があるはずです。

  • tempFileNameretainプロパティであり、それに の結果を代入しますalloc/init。漏れます。

  • 不変の空の文字列 ( [[NSString alloc] init]) はほとんど役に立ちません。そして、実際にstringFromFileAtPathはリークされています(技術的には実装の詳細に関しては、空の不変のシングルトン文字列があるため、実際のリークはありませんが....それでも...)

  • 最後に、クラッシュ:readLineメソッドは自動解放されたオブジェクトを正しく返します。しかし、while()戻り値を消費するループreadLinereleaseその戻り値を使用しているため、二重解放が発生し、すでに解放されているものを解放しようとします。

コードを「ビルドして分析」する必要があります。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。楽器を使用します。

于 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 に答える