0

Here is what I want to achieve. 1. Searching all the files 2. find all .jpg files during the searching 3. save all .jpg file paths into NSMutableArray

Here are the codes:

  1. Created the NSMutableArray:

    NSMutableArray *jpgFiles = [[[NSMutableArray alloc]init]autorelease]; 
    
  2. Searching all the parent folders under (/Users/) path (Start NSThread in here):

    NSString* filePath = [url path];
    NSArray *dirFiles = [[NSFileManager defaultManager]contentsOfDirectoryAtPath:filePath error:nil];
    
    if([dirFiles count]!=0)
    {
       for (int j=0; j<[dirFiles count]; j++) {
    
        NSString* pathExtension = [[dirFiles objectAtIndex:j] pathExtension];
    
        //if extension is null, we forwards to next level.
        if ([pathExtension isEqualTo:@""])
        {
            @autoreleasepool {
                [NSThread detachNewThreadSelector:@selector(searchingPicture:) toTarget:self withObject:[filePath stringByAppendingPathComponent:[dirFiles objectAtIndex:j]]];
            }
        }
        else
        {
            //if find jpg in this level, save into array
            if([pathExtension isEqualTo:@"JPG"])
            {
                [jpgFiles addObject:[filePath stringByAppendingPathComponent:[dirFiles objectAtIndex:j]]];
            }
        }
      }
    }  
    
  3. Keep searching the rest of sub folders and save proper file path into array:

    -(void)searchingPicture:(NSString*)path
     {
       NSFileManager *fileManager = [[[NSFileManager alloc] init] autorelease];
    
       NSURL *directoryURL = [NSURL URLWithString:[path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
    
       NSArray *keys = [NSArray arrayWithObject:NSURLIsDirectoryKey];
    
       NSDirectoryEnumerator *enumerator = [fileManager
                                     enumeratorAtURL:directoryURL
                                     includingPropertiesForKeys:keys
                                     options:0
                                     errorHandler:^(NSURL *url, NSError *error) {
                                         // Handle the error.
                                         // Return YES if the enumeration should continue after the error.
                                         return YES;
                                     }];
    
      for (NSURL *url in enumerator) {
          NSError *error;
          NSNumber *isDirectory = nil;
          if (! [url getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:&error]) {
               // handle error
           }
           else if (! [isDirectory boolValue]) {
              // No error and it’s not a directory; do something with the file
              if([[[url path] pathExtension]isEqualTo:@"JPG"])
              {
                  //This line gives me error !!!
                  [jpgFiles addObject:[url path]];
              }
           }
        }    
     }
    
  4. Error: (At beginning, it works fine and save many different files into array, but after saved around 50 files it starts to give me error and crash at the end).

Here is the correct element adds into array:

    /Users/NAME/Documents/Graduate Books/IMG_2517.JPG

Here is the error message:

    -[NSPathStore2 addObject:]: unrecognized selector sent to instance 0x10011d4d0

However, even this error occurs, it still keeps saving some of paths into array and then it will throw another error:

    An uncaught exception was raised

Could you guys tell me how to fix it?? Thanks !!

YU FENG
  • 888
  • 1
  • 12
  • 29
  • The error indicates a memory management issue. Perhaps the problem is the use of `autorelease` on `jpgFiles`. Instead of using `autorelease` when you create it, call `release` on it when you are really done with the array. – rmaddy May 16 '13 at 03:16
  • You are correct. Thanks !!!! :] But why should I use release instead of autorelease? – YU FENG May 16 '13 at 03:31
  • That's a big question. This overly simple reason is that an autoreleased object will be cleaned up at the end of the current run loop. See the docs for `NSObject autorelease`. There should be links to some other documentation that covers this in depth. – rmaddy May 16 '13 at 03:34

2 Answers2

1

First, trying to increase performance by randomly spawning threads is guaranteed failure. Concurrency must be considered and controlled.

Secondly, trying to decrease execution time of code that is accessing a slow resource (like the filesystem) by concurrently accessing said resource without constraint will be slower than serialized access. I/O to filesystems is relatively slow and linear I/O is always faster than concurrent, conflicted, random I/O.

Finally, NSMutableDictionary is not thread safe. Nor are the other mutable collection classes. If you are shoving stuff into collections from multiple threads, you'll see undefined behavior (typically crashes).

bbum
  • 162,346
  • 23
  • 271
  • 359
  • So my understanding is not using thread when I use filesystem. So would you mind brief introduce me what kind of situations are good to use NSThread. Thanks – YU FENG May 16 '13 at 13:02
  • Concurrency -- threads or queues -- is hard. These days, you'd probably not use NSThread at all but use NSOperationQueue or GCD. Where it is applicable is the subject of entire books! I would suggest starting here: https://developer.apple.com/library/mac/#documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html – bbum May 16 '13 at 14:33
0

NSMutableArray is not thread safe. It is thread safe when you guard it correctly -- so that no more than one thread is able to use it any time.

To illustrate:

- (void)addPath:(NSString *)pPath
{
  [self.lock lock];
  [self.files addObject:pPath];
  [self.lock unlock];
}

- (NSUInteger)countPaths
{
  [self.lock lock];
  const NSUInteger count = self.files.count;
  [self.lock unlock];
  return count;
}

- (NSArray *)copyPaths
{
  [self.lock lock];
  NSArray * paths = [self.files copy];
  [self.lock unlock];
  return paths;
}

And as bbum pointed out, directory enumeration as seen in your example is not a problem which lends itself well to parallelization -- Parallelization hurts in this scenario. A more practical approach would be to enumerate from just one thread. If you want to immediately load some of the images, just load them from the "I/O thread" as well.

justin
  • 104,054
  • 14
  • 179
  • 226