CocoaDev

Edit AllPages

L0Worker is a class that handles working asynchronously while keeping the main thread informed of the progress of the operation (a very common thing to do – see Finder, General/StuffitExpander, General/DiskUtility…).

To use it, simply subclass it like this:

@interface General/SomeWorkerClass : L0Worker { } @end

@implementation General/SomeWorkerClass

@end

/* … somewhere else, in your GUI code … */

General/SomeWorkerClass* worker = General/[SomeWorkerClass new]; [worker setDelegate:self]; [worker detachWorkerThread];

/* … let it do its work or call [worker cancel] to stop it … */


L0Worker.h

// Created by Emanuele on 06/09/05. // Copyright 2005 Emanuele Vulcano. Permission granted to everyone to use or modify this // code for any purpose.

#import <Cocoa/Cocoa.h>

/* kL0WorkerIndefiniteProgress Signals that it’s currently impossible to determine progress information for the work. See -setProgress: */ #define kL0WorkerIndefiniteProgress (-1.0f)

/* L0Worker

A L0Worker instance (or simply a worker) is an object that encapsulates
code for a time-consuming operation and provides common code to run the
operation synchronously or asynchronously. A delegate object may be
provided to receive messages as the operation progresses or concludes itself. */ @interface L0Worker : General/NSObject {
General/NSLock* objectLock;

float progress;
General/NSString* status;
BOOL cancelWasInvoked;
id delegate;

BOOL isSynchronous;
BOOL isRunning; }

/* -work To be overridden by subclasses. Executes the time-consuming operation and returns a final status in the form of a General/NSError* object (or nil if no error happened).

You can use -cancelWasInvoked to check whether another thread or the delegate tried
to cancel the operation and -setProgress: to communicate a new operation progress
value to the delegate.

Users of the worker shouldn't call -work directly; instead, -run or -detachWorkerThread
will set up the worker and call this method synchronously or asynchronously as needed. */ - (General/NSError*) work;

/* -run Executes the operation synchronously. In synchronous mode, the -work method will block when delegate messages are sent (that is, when a delegate message is sent, operation will pause until the delegate method returns). */

/* -detachWorkerThread Detaches a new thread and executes the operation in that thread. Messages sent to the delegate during the operation will still be sent on the main thread, not in this new thread. Note that, in asynchronous mode, messages sent by the -work method will not cause the operation to pause; the -work method will continue executing as delegate messages are received and their associated code, if any, is executed on the main thread. */

/* -cancelWasInvoked Should be called inside of -work only. Returns YES if an outside client called -cancel since the beginning of the operation, NO otherwise. */

/* -cancel Causes a canceling flag to be activated in the receiver. The -work method can check this flag with the -cancelWasInvoked method to terminate the operation, if possible. This method can be called: * during a synchronous operation, in the code of any delegate message. * during an asynchronous operation, by code running in any thread, including the main thread. */

/* -progress, -setProgress: Returns and sets the progress value for the operation. -progress can be called to read this value from any thread, including the main thread. -setProgress:, instead, should not be called by code outside this instance’s -work method.

The progress value must either be kL0WorkerIndefiniteProgress (signaling that
the progress value is unknown or cannot be computed), or a float value between 0.0f and 1.0f.
The default progress value after initialization is kL0WorkerIndefiniteProgress.

The value is only meaningful while the worker is performing the operation (that is, while
the -isRunning method returns YES). */ - (float) progress; - (void) setProgress:(float) progress;

/* -delegate, -setDelegate: Returns and sets the delegate for this object. Both these methods are thread-safe and can be called while the operation is being performed. */

/* -isRunning Returns YES as long as the worker is in any of these situations: * is preparing itself to begin an operation, after initialization ends; or * is actually executing the operation; or * is executing cleanup code after the operation, including the delegate message -worker:didEndWorkingWithFinalStatus:canceled:. If not in any of these situations, -isRunning returns NO.

The values of some of the methods of a worker object (notably -progress and -cancelWasInvoked)
are only meaningful while the worker is running. */ - (BOOL) isRunning;

@end

@interface General/NSObject (L0WorkerDelegate)

/* -worker:didUpdateProgress: Sent whenever a new progress value becomes available. */

/* -worker:didEndWorkingWithFinalStatus:canceled: Sent whenever the operation of a worker ends. If the worker executed its operation successfully, err will equal nil; otherwise, it will be whatever error was returned by the worker. The canceled parameter will be true if -cancel was called on the worker at least once since the beginning of the most recent operation. */

@end


L0Worker.m

// Created by Emanuele on 06/09/05. // Copyright 2005 Emanuele Vulcano. Permission granted to everyone to use or modify this // code for any purpose.

#import “L0Worker.h”

@implementation L0Worker

}

// to be overridden by subclasses

// invoked on the main thread via performSelectorOnMainThread:… // to send the -worker:didEndWorkingWithFinalStatus:canceled: delegate message.

// Simply calls L0pvt_startWorking: on a separate thread…

// …sets up an autorelease pool and the object lock (making all accessors // thread-safe) and then calls -run.

/* * * * * * * * * * * * * * * * * */

/* * * * * * * * * * * * * * * * * */

// both of these methods are thread-safe – they can even // change the delegate correctly while -work is running!

/* * * * * * * * * * * * * * * * * */

// throws an exception if newProgress isn’t what we need it to be // (newP != kL0WorkerIndefiniteProgress) || (newP <= 0.0f) || (newP >= 1.0f)

// YES if -run’s flags are set, NO otherwise.

@end