YU FENG
YU FENG

Reputation: 888

Objective c: Getting error when adding object into NSMutableArray during threading

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 !!

Upvotes: 0

Views: 269

Answers (2)

justin
justin

Reputation: 104698

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.

Upvotes: 0

bbum
bbum

Reputation: 162712

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).

Upvotes: 2

Related Questions