views:

631

answers:

2

Hi,

i'm new to xcode / cocoa and even objective-c thus my question might be stupid. Im trying to write a program that will hash files in a folder. I looked around and found a way to load a file via a NSData object and than hash it's bytes with CC_SHA512.

If i try to hash a few more files i noticed my memory running out. Using the Run -> Performance Tools i could pinpoint my problem. All NSData Objects i created are still in memory. I tryed autorelease and manualy release with release / dealloc. Nothing is working.

My Compiler Settings are standard with one exception i choose Objective-C Garbage Collection = required.

Maybe someone can show me what i'm doing wrong.

Here is the code:

-(FileHash*) hashFileByName :(NSString*) filePath{

    //NSData* inputData = [inputStr dataUsingEncoding:NSUTF8StringEncoding];
    NSLog(filePath);
    NSData* inputData = [[NSData dataWithContentsOfFile:filePath] autorelease];
    unsigned char outputData[CC_SHA512_DIGEST_LENGTH];
    CC_SHA512([inputData bytes], [inputData length], outputData);


    NSMutableString* hashStr = [NSMutableString string];
    int i = 0;
    for (i = 0; i < CC_SHA512_DIGEST_LENGTH; ++i)
     [hashStr appendFormat:@"%02x", outputData[i]];


    //NSLog(@"%@ hash : %@",filePath,hashStr);

    FileHash *hash = [[[FileHash alloc]init]autorelease];
    [hash setFileHash:hashStr];
    [hash setFilePath:filePath];
    [inputdata release];
    [inputdata dealloc];
    return hash; 
}

-(NSMutableArray*) hashFilesInDirectory:(NSString*) pathToDirectory:(Boolean) recursive : (IBOutlet id) Status : (Boolean*) BreakOperation{

    NSGarbageCollector *collect = [NSGarbageCollector defaultCollector];

    NSMutableArray *files;
     files = [[self listFilesOnlyRecursive:pathToDirectory] autorelease];

    NSMutableArray *hashes = [[[NSMutableArray alloc]init]autorelease];

    for (NSString *file in files) {

     [hashes addObject: [self hashFileByName:file]]; 
     [collect collectExhaustively];
    }


    return hashes;
}

-(NSMutableArray*) listFilesOnlyRecursive : (NSString*) startDir {

    NSMutableArray *filelist = [[[NSMutableArray alloc] init]autorelease];

    //Inhalt eines Verzeichnisses auflisten (unterverzeichnisse werden ignoriert
    NSFileManager *manager = [[NSFileManager defaultManager]autorelease];

    NSDirectoryEnumerator *enumerator = [manager enumeratorAtPath:startDir];
    int count = 0;
    id file;
    while (file = [enumerator nextObject])
    {

     //  file = [[[[startDir stringByAppendingString:@"/"]autorelease] stringByAppendingString:file] autorelease
     //    ];
     file = [NSString stringWithFormat:@"%@/%@",startDir,file];
     BOOL isDirectory=NO;
     [[NSFileManager defaultManager] fileExistsAtPath:file isDirectory:&isDirectory];
     if (!isDirectory){
      [filelist addObject:file];
      //printf("\n:%s:\n",[file UTF8String]);
      count++;
     }


    }
    NSLog(@"Es waren %i files",count);
    return filelist;
}

All of this is started by

int main(int argc, char *argv[])
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    //return NSApplicationMain(argc,  (const char **) argv);
    MemoryLeakTest *test = [[[MemoryLeakTest alloc]init]autorelease];
    [test hashFilesInDirectory:@"/huge directory/" :YES :nil :nil];
    [pool drain];
    [pool release];
    [pool dealloc];

}

Maybe someone has an idea.

Than You in advance :) Nubus

+2  A: 

A couple of things:

NSData* inputData = [[NSData dataWithContentsOfFile:filePath] autorelease];

You must retain this if you want to keep it around past the current method execution (this is a bit oversimplified, but reasonable for your example). Since dataWithContentsOfFile: doesn't contain alloc, copy, or new you're not responsible for releasing it unless you explicitly retain it. Given that you're only using it locally in the function, you don't need to retain this. Thus, just use the following and don't call release/autorelease or dealloc on it:

NSData* inputData = [NSData dataWithContentsOfFile:filePath];

Further, you never manually dealloc things. Just release/autorelease them as needed. dealloc will get called as required.

[inputData dealloc]; // don't do this explicitly

You definitely need to read the Cocoa Memory Management document. It will clear up a lot.

nall
Hi,thanks for the link. The Documentation states that if i use convenience methods i don't have to use release as you said. The NSData Object is not used outside the hashFileByName function. I really have no idea how to get rid of the NSData objects.(I'm not through the Documentation jet) Is there a way to force the removal of such an object?Thank You
TheNubus
Convenience methods give you back an autoreleased object. That means (assuming its retain count is zero) it will be reclaimed in memory at the next autorelease collection point. If you want to be more explicit about it, you'll need to alloc/init/release.
nall
A: 

After reading the MemoryManagement Documentation i knew as much as before. So i started try and error. I tried things like release the NSData Object til the retaincount is 0.. and and and. Than i found a working solution.

I had to init the NSData Object myself, and set it to auto release (i was not able to release it myself because after i called init there was a retaincound of 2 and trying to release it 2 times leads to a crash

blabla here my solution:

-(FileHash*) hashFileByName :(NSString*) filePath{

    NSAutoreleasePool *innerpool = [[NSAutoreleasePool alloc]init];

    //NSData* inputData = [inputStr dataUsingEncoding:NSUTF8StringEncoding];
    NSData* inputData = [[[NSData alloc]initWithContentsOfFile:filePath] autorelease];
    unsigned char outputData[CC_SHA512_DIGEST_LENGTH];
    CC_SHA512([inputData bytes], [inputData length], outputData);


    NSMutableString* hashStr = [NSMutableString string];
    int i = 0;
    for (i = 0; i < CC_SHA512_DIGEST_LENGTH; ++i)
     [hashStr appendFormat:@"%02x", outputData[i]];



    [innerpool drain];

    //NSLog(@"%@ hash : %@",filePath,hashStr);

    FileHash *hash = [[[FileHash alloc]init]autorelease];
    [hash setFileHash:hashStr];
    [hash setFilePath:filePath];

    return hash; 
}

I hope this will help someone :)

Thank you for your answers.

ps: maybe soneone can tell me why i couldn't release it myself or why there where a retain count of 2.

TheNubus