ThreadWorker |
v0.7.1 - Throws a task onto another thread and notifies you when it's done.
Superclass: NSObject
Declared In: ThreadWorker.h
Usage:
[ThreadWorker workOn:self withSelector:@selector(longTask:) withObject:someData didEndSelector:@selector(longTaskFinished:) ];
The ThreadWorker class was designed to be simple and to make multi-threading even simpler. You can offload tasks to another thread and be notified when the task is finished.
In this sense it is similar to the Java SwingWorker class, though the need for such a class in Cocoa and Objective-C is as different as the implementation.
Be sure to copy the ThreadWorker.h and ThreadWorker.m files to your project directory.
To see how to use this class, see the documentation for the "workOn" method below.
Consider adding the file release feed to your news aggregator (if you use one) to be notified of new releases: Follow the file release RSS feed...
I'm releasing this code into the Public Domain. Do with it as you will. Enjoy!
Original author: Robert Harder, rob@iharder.net
Change History
0.7.1 - Modified example to build in Xcode 3.2. 0.7 - o Added ability to mark thread as cancelled. o Changed the behavior when "longTask" takes a second argument. Instead of passing a proxy to the primary thread's "self" it passes a references to the ThreadWorker. The recommended way to pass information from the primary, or originating, thread is to use an NSDictionary to pass in the Things You'll Need. See the Controller.m example. o Changed thread's termination behavior so that as soon as your "longTask:" is finished, the thread will exit. This means if you left anything on the NSRunLoop (or more likely an NSURL did it without your knowledge), it will get dumped. 0.6.2 - Moved [super dealloc] to the end of the dealloc method and ensured "init" returns nil if it fails. 0.6.1 - Added [super dealloc] to the dealloc method and moved the dealloc declaration out of the private API and into the public .h file as it should be. 0.6 - Eliminated the need for the runSelectorInCallingThread method by making a proxy to the target available to the task working in the new thread. This makes for much less overhead. Also changed static method signature from withArgument to withObject. 0.5.1 - Oops. Forget a necessary thread lock for the NSConnection creation. 0.5 - Uses NSConnection to communicate between threads, so although we might have been thread-safe before (depending on whether or not addTimer in NSRunLoop is thread-safe), we're definitely thread-safe now - or so we think. =) In the process we had to do away with the helper functions that took a bit of hassle out using runSelectorInCallingThread with arguments that are not objects. Sorry. 0.4.1 - Fixed some typos in commented sections. 0.4 - Released into the Public Domain. Enjoy! 0.3 - Permitted "workOn" method to accept a second argument of type ThreadWorker* to allow for passing of the parent ThreadWorker to the secondary thread. This makes it easy and reliable to call other methods on the calling thread. 0.2 - Added runSelectorInCallingThread so that you could make calls back to the main, i.e. calling, thread from the secondary thread. 0.1 - Initial release.
Returns whether or not someone has tried to cancel the thread.
Make sure we clean up after ourselves.
Just a little note to say, "Good job, Rob!"
Mark the ThreadWorker as cancelled.
Call this class method to work on something in another thread.
cancelled |
Returns whether or not someone has tried to cancel the thread.
-(BOOL)cancelled;
Returns whether or not someone has tried to cancel the thread.
dealloc |
Make sure we clean up after ourselves.
- (void) dealloc;
Make sure we clean up after ourselves.
description |
Just a little note to say, "Good job, Rob!"
+ (NSString *)description;
Just a little note to say, "Good job, Rob!" to the original author of this Public Domain software.
markAsCancelled |
Mark the ThreadWorker as cancelled.
-(void)markAsCancelled;
Marks the ThreadWorker as cancelled but doesn't actually cancel the thread. It is up to you to check whether or not the ThreadWorker is cancelled using a two-argument "longTask:..." method like so:
- (id)longTask:(id)userInfo anyNameHere:(ThreadWorker *)tw { ... while(... && ![tw cancelled]){ ... } }
workOn:withSelector:withObject:didEndSelector: |
Call this class method to work on something in another thread.
+ (ThreadWorker *) workOn:(id)target withSelector:(SEL)selector withObject:(id)userInfo didEndSelector:(SEL)didEndSelector;
target
The object to receive the selector message. It is retained.
selector
The selector to be called on the target in the worker thread.
userInfo
An optional argument if you wish to pass one to the selector and target. It is retained.
didEndSelector
An optional selector to call on the target. Use the value 0 (zero) if you don't want a selector called at the end.
Returns an autoreleased ThreadWorker that manages the worker thread.
Example:
NSDictionary *thingsIllNeed = [NSDictionary dictionaryWithObjectsAndKeys: self, @"self", myProgressIndicator, @"progress", myStatusField, @"status", nil]; [ThreadWorker workOn:self withSelector:@selector(longTask:) withObject:thingsIllNeed didEndSelector:@selector(longTaskFinished:)];
The longTask method in self will then be called and should look something like this:
- (id)longTask:(id)userInfo { // Do something that takes a while and uses 'userInfo' if you want id otherSelf = [userInfo objectForKey:@"self"]; NSProgressIndicator *progress = (NSProgressIndicator *)[userInfo objectForKey:@"progress"]; NSTextField *status = (NSTextField *)[userInfo objectForKey:@"status"]; return userInfo; // Will be passed to didEndSelector }
Optionally you can have this "longTask" method accept a second argument which will be the controlling ThreadWorker instance which you can use to see if the ThreadWorker has been marked as cancelled. Your "longTask" method might then look like this:
- (id)longTask:(id)userInfo anyNameHere:(ThreadWorker *)tw { ... while(... && ![tw cancelled]){ ... } }
You can name the second parameter anythign you want. You only have to match it when you create the ThreadWorker like so:
[ThreadWorker workOn:self withSelector:@selector(longTask: anyNameHere:) withObject:userInfo didEndSelector:@selector(longTaskFinished:)];
When your longTask method is finished, whatever is returned from it will be passed to the didEndSelector (if it's not nil) as that selector's only argument. The didEndSelector will be called on the original thread, so if you launched the thread as a result of a user clicking on something, the longTaskFinished will be called on the main thread, which is what you need if you want to then modify any GUI components. The longTaskFinished method might look something like this, then:
- (void)longTaskFinished:(id)userInfo { //Do something now that the thread is done // ... }
Of course you will have to have imported the ThreadWorker.h file in your class's header file. The top of your header file might then look like this:
import <Cocoa/Cocoa.h> import "ThreadWorker.h"
Enjoy.
Last Updated: Thursday, October 22, 2009