Hi James,

I recently finished a file routing app that does a lot of heavy duty operations 
to files and found that the combination of 
NSInvocationOperations/NSOperationQueue to be really programmer friendly. 

Among the stuff my app does is copy large image files from A to B, creates 
thumbnails and grayscale versions of them and writes the info to a database and 
with NSOperationQueues, there is basically no slowdown at all in the user 
interface. Queues can be stopped at any time so you could give the user that 
option. Here's a stripped down skeleton of how I'm using 
NSOperationQueue/NSInvocationOperation which might get you up and running with 
queues a bit faster. The key is, they are really forgiving and simple to use.

Of particular interest to you might be the delegate idea which allows the file 
processing method to continuously update and send info to another process which 
would help with your progress indicator issue

I just did a test with 1258 full sized images and it routed them, created 
thumbnails, grayscales and extracted a bunch of info in 3 minutes 24 seconds 
with no effect on user responsiveness of the machine or the app.

Enjoy

// In this example, your delegate in the user interface code, might have a 
handler defined like so
- (void) handleImageInfo:(id) inMessage
{
        if ([inMessage isKindOfClass: [NSNumber class]])
                // do something with the file count
        else if ([inMessage isKindOfClass: [NSDictionary class]])
                // add data from inMessage to your user interface
}


// Class that monitors and processes files in a drop folder
// For you, it might be a user selected scanning folder
- (id) init
{
        self = [super init];
        if (self)
        {
                queue           = [[NSOperationQueue alloc] init];
                delegate        = nil;
                selector        = nil;
                return self;
        }
        
        [self release];
        return nil;
}

- (void) setDelegate:(id) inDelegate
                selector:(SEL) inSelector
{
        delegate                = [inDelegate retain];
        selector                = inSelector;
}


- (void) start
{
        [self queueOperation];
}

- (void) stop
{
        [queue setSuspended: YES];
}

- (void) queueOperation
{
        NSInvocationOperation   *operation;
        
        operation       = [[[NSInvocationOperation alloc] 
                                                initWithTarget: self
                                                
selector:@selector(processDropFolder) 
                                                object: nil]
                                                autorelease];
                                                
        [operation addObserver: self
                        forKeyPath: @"isFinished"
                        options: NSKeyValueObservingOptionNew
                        context: NULL];
                        
        [queue addOperation: operation];
}

- (void) processDropFolder
{
        NSArray         *fileNames      = 
UtilContentsOfDirectoryAtPath(dropFolder);
        
        if ((fileNames != nil) && ([fileNames count] > 0))
        {
                NSNumber *fileCount             = [NSNumber numberWithInt: 
[fileNames count]];
                
                // send file count off to delegate
                [delegate performSelector: selector withObject: fileCount];

                // we have some photos to process
                NSEnumerator    *e      = [fileNames objectEnumerator];
                NSString                *filePath;
                NSMutableDictionary     *fileInfo;
                
                NSLog(@"Scanning: %@", dropFolder);
                
                while (fileName = [e nextObject])
                {
                        // ignore invisible files
                        if (![fileName hasPrefix: @"."])
                        {
                                filePath                = [dropFolder 
stringByAppendingPathComponent: fileName];
                                
                                // read the exif info, find (or create) a 
thumbnail
                                // package it up in a nice NSDictionary and 
send it 
                                // to your delegate
                                NSDictionary *imgInfo   = 
UtilImageInfo(filePath);
                                
                                if (imgInfo != nil)
                                        [delegate performSelector: selector 
withObject: imgInfo];
                        }
                }
        }
}

- (void)observeValueForKeyPath:(NSString *) inKeyPath 
                ofObject:(id) inObject 
                change:(NSDictionary *) inChange 
                context:(void *) inContext
{
        if ([inKeyPath isEqualToString: @"isFinished"])
        {
                // remove invocation observer
                [inObject removeObserver: self forKeyPath: @"isFinished"];
                
                // queue up another pass
                [self queueOperation];
        }
}

// utility function that reads info from an image file
NSMutableDictionary *UtilImageInfo(NSString *inPath)
{
        NSWorkspace                     *workspace      = [NSWorkspace 
sharedWorkspace];
        NSError                         *error          = nil;
        NSString                        *type           = [workspace 
typeOfFile: inPath error: &error];
                        
        if ((error == nil) && [workspace type: type conformsToType: (NSString 
*) kUTTypeImage])
        {
                // inPath is some sort of image
                NSURL                           *url    = [NSURL 
fileURLWithPath: inPath];
                CGImageSourceRef        imgSrc  = 
CGImageSourceCreateWithURL((CFURLRef) url, NULL);
                
                if (imgSrc != nil)
                {
                        NSDictionary    *props  = (NSDictionary *) 
CGImageSourceCopyPropertiesAtIndex(imgSrc,0, NULL);
                        NSMutableDictionary     *result = [NSMutableDictionary 
dictionary];
                
                        // copy out whatever info you're interested from props
                        NSNumber        *zero                   = [NSNumber 
numberWithInt: 0];
                        NSString        *colorModel             = [props 
objectForKey: @"ColorModel"],
                                                *colorProfile   = [props 
objectForKey: @"ProfileName"],
                                                *pixelWidth             = 
[props objectForKey: @"PixelWidth"],
                                                *pixelHeight    = [props 
objectForKey: @"PixelHeight"],
                                                *bitsPerPixel   = [props 
objectForKey: @"Depth"];
                                                                        
                        [result setObject: ((colorModel == nil) ? @"" : 
colorModel) forKey: @"color"];
                        [result setObject: ((colorProfile == nil) ? @"" : 
colorProfile) forKey: @"profile"];
                        [result setObject: ((pixelWidth == nil) ? zero : 
[NSNumber numberWithFloat:[pixelWidth floatValue]]) forKey: @"width"];
                        [result setObject: ((pixelHeight == nil) ? zero : 
[NSNumber numberWithFloat:[pixelHeight floatValue]]) forKey: @"height"];
                        [result setObject: ((bitsPerPixel == nil) ? zero : 
[NSNumber numberWithFloat:[bitsPerPixel floatValue]]) forKey: 
@"bits_per_pixel"];
                                                                        
                        CFRelease(imgSrc);
                        [props release];
                        
                        return result;
                }
        }
        
        return nil;
}


_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to