开发者

How to optimize directory listing? (enumeratorAtPath and recursive call contentsOfDirectoryAtPath)

I wrote a method(getDirTree1) that makes listing all directories from the roo开发者_开发知识库t, using the recommended class NSDirectoryEnumerator and method nextObject. But while it was running unacceptably used a lot of memory (mainly private class NSPathStore2):

-(void) getDirTree1:(NSString*)directoryPath {
    NSDirectoryEnumerator *dirEnum = [self->fileManager enumeratorAtPath:derectoryPath];
    NSString *filePath;
    NSString *fullFilePath;

    while ( ( filePath = [ dirEnum nextObject ] ) != nil ) {
        fullFilePath = [ directoryPath stringByAppendingPathComponent:filePath ];
        NSLog( @"%@ \n", fullPath ); 
}

}

Assuming that this is because the object NSDirectoryEnumerator, I rewrote the method(getDirTree2). Now using the recursion, NSArray class and objectEnumerator method. But once again used a lot of memory.

-(void) getDirTree2:(NSString*)directoryPath {
   NSArray *contents = [ self->fileManager contentsOfDirectoryAtPath:directoryPath error:NULL ];
   NSEnumerator *enumeratorContent [ contents objectEnumerator ];
   NSString *file;
   BOOL fileIsDirectory = FALSE;

   while ( ( file = [ enumeratorContent nextObject ] ) ) {
       NSLog( @"%@ \n", [ directoryPath stringByAppendingPathComponent: file ] );
       if ( [ self->fileManager fileExistAtPath:[ directoryPath stringByAppendingPathComponent:file ] isDirectory:&fileIsDirectory ] && fileIsDirectory )
           [ self getDirTree2:[ directoryPath stringByAppendingPathComponent: file ] ];
   }
}

What I missed(maybe I must dealloc/retain some objects) and how to do better. Thanks.


[directoryPath stringByAppendingPathComponent:filePath]; returns an autoreleased object. Since it's happening inside such a tight loop, all these objects are adding up and causing the large memory footprint in question. All you have to do to fix it is get rid of them more often. You can change your method to something that doesn't use autorelease, or you could just create your own, tight autorelease pool, like this:

while ( ( filePath = [ dirEnum nextObject ] ) != nil ) {
    NSAutoreleasePool* pool = [NSAutoreleasePool new];
    fullFilePath = [ directoryPath stringByAppendingPathComponent:filePath ];
    NSLog( @"%@ \n", fullPath );
    [pool drain];
}

This will ensure that everything's released as soon as you no longer need it, avoiding the buildup of objects during the loop.

(Interesting sidenote: NSPathStore2 is a private class related to NSString (which is a class cluster) used to store path-type strings. That's how I knew which method was at fault.)


For anyone using automatic reference counting, if the following (which should be the equivalent of andyvn22's answer for ARC) doesn't work for you:

while ( ( filePath = [ dirEnum nextObject ] ) != nil ) {
    @autoreleasepool {
        fullFilePath = [ directoryPath stringByAppendingPathComponent:filePath ];
        NSLog( @"%@ \n", fullPath );
    }
}

I was able to solve it by doing this instead

filePath = [ dirEnum nextObject ] );
while ( filePath != nil ) {      
    @autoreleasepool {
        fullFilePath = [ directoryPath stringByAppendingPathComponent:filePath ];
        NSLog( @"%@ \n", fullPath );
        filePath = [ dirEnum nextObject ] );
    }
}

The code isn't as elegant as it could be, but the memory saving is.


Update: Recently had problems with this again, what worked even better was this:

file = [dirEnum nextObject];
while (file) {
    @autoreleasepool {
        fullFilePath = [ directoryPath stringByAppendingPathComponent:filePath ];
        NSLog( @"%@ \n", fullPath );
        filePath = [ dirEnum nextObject ] );
    }
}


Easily use NSDirectoryEnumerator as documented here and shown here

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜