Implementing background worker - Obj C/Cocoa - objective-c

I have 2 windows in my cocoa app. Main window opens a sub window. On click of OK on the sub window, I invoke a deligate on main form which will tell that OK button is clicked on the sub window.
Now, I need to run a long running process on the main window "in the background" so that the window will not become unresponsive. I also have progress bar which should show progress of this long running process.
Please let me know, what is the best way to achieve this.

You should start with Apple's Concurrency Programming Guide.
Specially the section about NSOperationQueue.

You can use Grand Central Dispatch for this. First you create a dispatch queue which will contain operations you want to perform on another thread. Each operation is represented as a Objective-C block (closure).
First you get a queue to put the task you want to run on another thread.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
Then you place a block representing the work you want to do on this queue:
dispatch_async(queue, ^{
// this happens on separate thread
NSImage *image = produceImageFromSomeReallySlowOperation()
dispatch_async(dispatch_get_main_queue(), ^{
// this happens on main thread
[myView setImage:image];
});
});
The dispatch_get_main_queue() function returns the queue which is used for operations on the main thread (where the GUI is executed). This means that [myView setImage:image] will be executed on the main thread. You can place your update of the progress bar here. Just dispatch on the main queue to update the progress at every point in your algorithm where it makes sense to do so.
All of this can also be performed with NSOperation which provides a higher level Objective-C interface to the same functionality. But using GCD directly is sometimes easier. It depends on what you want to do.

Related

Objective C Multi thread NSWindow alternative?

I keep crashing when doing:
[NSWindow orderFront:nil]
From a thread I spawned in my app. Is working with UI elements from thread not possible like GTK+?
Edit:
oh goodness just found this:
https://stackoverflow.com/a/11900929/1828637
So apparently I cant use NSWindow from another thread, so objc is out, is it possible to do multi thread window stuff with CoreFoundation instead? I have to do from thread so Im looking for alternative way
You can only work with UI elements on the main thread.
I use GCD to ensure all UI activities are running on the correct thread:
dispatch_sync(dispatch_get_main_queue(), ^{
// Do your UI updates!
});
See why:
In Cocoa Touch, the UIApplication i.e. the instance of your application is attached to the main thread because this thread is created by UIApplicatioMain(), the entry point function of Cocoa Touch. It sets up main event loop, including the application’s run loop, and begins processing events. Application's main event loop receives all the UI events i.e. touch, gestures etc.
UI interaction always has to be done on the main thread.
You can simply dispatch the code in question with GCD on the main thread:
dispatch_async( dispatch_get_main_queue(), ^(void)
{
[NSWindow orderFront:nil];
});

Display UI, from a background thread without breaking the flow

I have a code executing in the background thread, which is performing some kind of computation and is within a do-while loop. Due to some changes in the requirements, I have to display a UI to prompt user for input. This UI code will have to be done in the main thread, and after the prompt is entered, the logic needs to continue. Using a dispatch_async on main thread, I can display the UI, but Step -2 should not continue, until the UI is done. What is the best way to accomplish this, without breaking the flow of the code and moving units into blocks?
For example:
-(void) compute
{
do
{
//calculate some data
// Step -1...
...
// Step -2
...
...
} while(flag)
}
Between Step 1 and Step 2, I want to display a prompt. What is the best way to do so? Is it okay, to block this background thread using a mutex, which will get fired, by the main thread after the UI is done?
For this I would use GCD (Grand Central Dispatch). You can easily execute a block synchronously on the main thread (or asynchronously if you prefer), using dispatch_sync (or dispatch_async). I personally use my wrapper class EX2Dispatch in my EX2Kit library (https://github.com/einsteinx2/EX2Kit), but it's the same thing.
As an example, you would do something like this:
dispatch_sync(dispatch_get_main_queue(), ^{
// Do some stuff to the UI
};
EDIT:
I was reading it as needing to display information to the user based on the earlier calculation, but if you need a response from the user before continuing, then the loop needs to break after showing the alert, then be called again.
You could use an instance variable to track how far into the loop you are, so that it can be resumed at the same point in the UIAlertView's button clicked delegate method.
Try this
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue, ^(void) {
//do stuff here
});

Better way to Trigger Asynchronous Callbacks in Objective-C

I am looking for a better way to do this, if possible.
I have an asynchronous callback that updates a local sqlite database. I set a flag in a singleton variable (archiveUpdateComplete) when the update completes. I sleep in a do-while until the flag gets set to true, then I hydrate my tableview. Would like to remove sleep()! Thanks for any suggestions.
#define kBgQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)
- (void)viewDidLoad
{
dispatch_async(kBgQueue, ^{
//Hydrate word archive table view
do {
sleep(1.0);
} while ([sharedManager archiveUpdateComplete]==NO);
[self performSelectorOnMainThread:#selector(hydrateWordArchive) withObject:nil waitUntilDone:YES];
//Run custom activity indicator
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:self.view animated:YES];
});
});
}
If you need to poll
Polling/sleeping is rarely necessary or good. As an alternative:
You can attach an NSTimer to the main thread's run loop.
The selector the timer calls can test [sharedManager archiveUpdateComplete]
if YES is returned, then
invalidate the timer
call [MBProgressHUD hideHUDForView:self.view animated:YES];
If you don't need to poll
There are a few immediate alternatives. Which you choose depends on what knows about what:
If your manager knows who to message following completion, then the manager can simply message it. If that must occur on the main thread you can use -[NSObject performSelectorOnMainThread:withObject:waitUntilDone:] to forward to the main thread. You may also see this approach with delegates. In the case of a singleton, it doesn't make a lot of sense to take this route.
If your manager does not know who is interested in the change/completion, your manager can post a NSNotification after the task has finished (on the current thread or from the main thread).
Key Value Observing (KVO) is another option.
Perhaps I'm missing something, but why don't you just use a completion callback for this?
In other words, you change your computation to "think" in terms of nested blocks. The first async block (on some concurrent queue) does the work of updating the database, and when it's done it dispatches another async block (to the same concurrent queue) which hydrates the tableview. Finally, from that block you dispatch_async yet another block on the main queue which updates the UI, since that's the only bit that needs to execute on the main queue.
Rather than poll, in other words, you want to chain your async operations. See COMPLETION CALLBACKS section of the man page for dispatch_async().

How to perform processing during an animation

I have an app where I want to have an image animation while I am reading some info from a database and building object. I have used UIImageView and set up and array of images, but if I start the animation and then do my DB processing, the animation does not play.
Is there another way to start the animation, or for me to do processing during the animation?
Thanks
It sounds like you're doing your processing on the main thread, which is preventing your animations from running.
Animations run on the main thread, so to avoid blocking this thread processing should be scheduled on a different thread.
You can achieve this using blocks like so:
dispatch_queue_t queue;
queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
// Your processing to be performed on this thread.
});
Or on earlier iOS versions, like so:
[self performSelectorInBackground:#selector(yourProcessing) withObject:nil];
- (void)yourProcessing {
// Your processing to be performed on this thread.
}
I highly recommend a read through of the Threading Programming Guide, followed by watching the WWDC sessions covering Blocks and Grand Central Dispatch (WWDC 2009).
You can run your DB processing on the background thread after you begin the animation. This will allow the two to happen simultaneously.
Sounds like you’re trying to do your database processing on the main thread, which, yes, will block your UI so it can’t animate (and so the user can’t interact with anything). Take a look at the Concurrency Programming Guide.

Code execution in objective c

HI all, if i've somthing like this:
my code....
// active indicator activity
[otherClass method]; // method that takes 5-6 seconds
// disable indicator activity
my code...
When the long method is called, in my class code is blocked right?
If i active an indicator activity before call the method, it will be animating while "method" is executing?
Thanks.
As iceydee mentions, the UI elements (like your activity indicator) run on the main thread. If you load a big file, download something or any other thing that takes time, you must execute that on other thread if you want to animate UI elements. You can use Grand Central Dispatch, performSelectorInBackGround or other techniques (not recommendable). I would make:
my code....
// active indicator activity
[otherClass performSelectorInBackground:#selector(method) withObject:nil]; // method that takes 5-6 seconds
my code...
Then in otherClass's method, stop the activity indicator on the main thread:
[activityIndicator performSelectorOnMainThread:#selector(stopAnimating) withObject:nil waitUntilDone:NO];
You should avoid blocking the main thread for that long, consider breaking the method into two - running [otherClass method] in a separate thread. The main thread is used for UI updates, unsure if the indicator will be able to operate with main thread blocked, I think not.
Yes, it will be blocked unless you run your long method in another thread.
To do this use a technique like this. see performSelectorInBackground and performSelectorOnMainThread.