DTWorker is a Cocoa framework that can be used to process arbitrary tasks in the background. Each DTWorker object manages a single thread. It is available through bitbucket. There is also an application that demonstrates how it works. Enjoy!
The DTWorker class manages a custom run loop source. Normally the run loop lies dormant, however it will wake up when the DTWorker responds to an NSNotification object (issued with a name the DTWorker object has already been registered with). Once this happens the user info dictionary supplied with the NSNotification object gets added to a queue, and then the run loop source will process the objects in the queue until it is empty. "Process a dictionary?" I hear you cry, well, not exactly, the only requirement is that under a pre-defined key, DTWorkerInvocationKey, an NSInvovation object is set, which then gets invoked on the thread. Additionally there is support for callbacks.
The DTWorkerViewController essentially acts as a delegate for all DTWorker objects that get registered with it, and will present their status in its view. The user therefore must set the view somewhere in their own code (such as the contentView of an NSBox).
Confused? Prefer to see some code? Assume we have a class such as the one below:
#import <Cocoa/Cocoa.h>
#import <DTWorker/DTWorker.h>
#import <DTWorker/DTWorkerViewController.h>
extern NSString *AddATask;
@interface AppController : NSObject {
IBOutlet NSBox *workerViewBox;
DTWorker *worker;
DTWorkerViewController *workerViewController;
}
@property(retain) DTWorkerViewController *workerViewController;
@end
In the initialize method, in this case awakeFromNib, as we're going to use IBOutlets for the NSBox, we'd create and setup the DTWorker and DTWorkerViewController objects as follows:
#import "AppController.h"
NSString *AddATask = @"AddATask";
@implementation AppController
@synthesize workerViewController;
- (void)awakeFromNib {
// Create the worker object, and register a notification name with with itÊ
self.worker = [[DTWorker alloc] initWithIdentifier:@"My Worker Object"];
[worker addNotificationName:AddATask];
// Create the worker view controller, and register the worker objectÊ
self.workerViewController = [[DTWorkerViewController alloc] init];
[workerViewController registerWorker: worker withOwner:self];
// Set the view
[workerViewBox setContentView:[workerViewController view]];
[workerViewBox setNeedsDisplay:YES];
}
@end
Then, to add a task (and trigger any processing), we just need to construct a dictionary. At a minimum we must include an NSInvocation object with the key DTWorkerInvocationKey (this is defined in the DTWorker header).:
- (IBAction)triggerWorker:(id)sender {
// A dictionary of arguments by the action
NSString *str = [NSString stringWithFormat:@"Task: %@", [[NSDate date] description]];
NSDictionary *actionArgs = [NSDictionary dictionaryWithObjectsAndKeys:str, @"Message", nil];
SEL selector = @selector(anAction:);
NSMethodSignature *sig = [self methodSignatureForSelector:selector];
NSInvocation *action = [NSInvocation invocationWithMethodSignature:sig];
[action retainArguments];
[action setTarget:self];
[action setSelector:selector];
[action setArgument:&actionArgs atIndex:2];
// Now post a notification
static NSNotificationCenter *nc = nil;
if(!nc)
nc = [NSNotificationCenter defaultCenter];
NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:
action, DTWorkerInvocationKey,
nil];
[nc postNotificationName:AddATask object:self userInfo:dic];
}
Where anAction:(NSDictionary *)props; is the "processor intensive" method that gets executed on the secondary thread. And that's pretty much all there is to it, other things such an info string, and callback methods will (soon) be fully discussed in headerdoc, and made available in the repository.