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 ([email protected])
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 [email protected]