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().
Related
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
});
I haven't used GCD or much threading in my apps but I've run into a situation where I need to run a method or two off another thread. Once this method completes I need to call another method using the main thread from a callback. I've been searching around to see how to detect when a thread has finished the operation but still not too clear on the subject.
I created a test app and just used the viewDidLoad method for a quick example.
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSLog(#"viewDidLoad called");
sleep(5); // simulating a thread being tied up for 5 seconds
dispatch_async(dispatch_get_main_queue(), ^{
[self callbackMethod]; // method called after above thread has completed running
});
});
}
Will this example work for what I'm trying to do? When running the application it appears that the callback method is called after the sleep(5) finishes. Is this the proper way of handling this situation or am I way off course?
You're spot on; that's the standard pattern for getting off and on the main thread. See my answer here: https://stackoverflow.com/a/13080519/341994
And for example code from my book, structured in this very way:
https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/bk2ch25p868mandelbrotGCD/ch38p1106mandelbrotNoThreading/MyMandelbrotView.swift
In that example, look at how drawThatPuppy gets off the main thread to do the time-consuming calculations and then back on the main thread to do the drawing into the interface.
I was having problems modifying a view inside a thread. I tried to add a subview but it took around 6 or more seconds to display. I finally got it working, but I don't know how exactly. So I was wondering why it worked and what's the difference between the following methods:
This worked -added the view instantly:
dispatch_async(dispatch_get_main_queue(), ^{
//some UI methods ej
[view addSubview: otherView];
}
This took around 6 or more seconds to display:
[viewController performSelectorOnMainThread:#selector(methodThatAddsSubview:) withObject:otherView
waitUntilDone:NO];
NSNotification methods - took also around 6 seconds to display the observer was in the viewController I wanted to modify paired to a method to add a subview.
[[NSNotificationCenter defaultCenter] postNotificationName:
#"notification-identifier" object:object];
For reference these were called inside this CompletionHandler of the class ACAccountStore.
accountStore requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error) {
if(granted) {
// my methods were here
}
}
By default, -performSelectorOnMainThread:withObject:waitUntilDone: only schedules the selector to run in the default run loop mode. If the run loop is in another mode (e.g. the tracking mode), it won't run until the run loop switches back to the default mode. You can get around this with the variant -performSelectorOnMainThread:withObject:waitUntilDone:modes: (by passing all the modes you want it to run in).
On the other hand, dispatch_async(dispatch_get_main_queue(), ^{ ... }) will run the block as soon as the main run loop returns control flow back to the event loop. It doesn't care about modes. So if you don't want to care about modes either, dispatch_async() may be the better way to go.
It's likely because performSelectorOnMainThread:withObject:waitUntilDone: queues the message with common run loop modes. According to Apple's Concurrency Programming Guide, the main queue will interleave queued tasks with other events from the app's run loop. Thus, if there are other events to be processed in the event queue, the queued blocks in the dispatch queue may be run first, even though they were submitted later.
This article is a superb explanation to performSelectorOnMainThread vs. dispatch_async, which also answers the above question.
Did you try thePerformSelectorOnMainThread with waitUntilDone=YES
Eg:
Code:
[viewController performSelectorOnMainThread:#selector(methodThatAddsSubview:) withObject:otherView waitUntilDone:YES];
I think that might solve the issue as of why the PerformSelectorOnMainThread takes so long to respond.
How can i make sure in Objective-C, that a function only get called, until another function is ready?
Update:
-(void)reloadJsonFromServer {
[[Sync sharedObject] synchronise];
[self reload];
}
I've got this function.
The second function "reload" should only be called, if the first function is – in this case it's a singlton – is ready. Ready means, that the first function is no more longer running.
So you want to wait on the completion of an asynchronous method? There's a whole bunch of ways to do that.
Make the synchronise method itself call reload on your object when it finishes
dispatch_async the reload method and have it just wait until the other method populates some flag or data structure that you are waiting on before continuing (BOOL synchronised or similar). Note that if your reload method does anything with UIKit, though, then you need to run it on the main thread.
Change the way synchronise runs so it doesn't actually return to the caller until it's done synchronising, but then dispatch_async the reloadJsonFromServer method.
Change synchronise as in the third point, but instead of using dispatch_async, add both of the method calls to an NSOperationQueue as NSOperations, with reload dependent on the completion of synchronise. The operation queue will handle it after that.
Those are just a few, I'm sure other people can suggest more.
In the last few days, i've learnd something about notifications.
I think that is a good way too, to handle something like this. For more information about this look at this blog entry.
Custom Events in Objective-C
A bit late, but with NSNotification it can be handled.
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserverForName:nil
object:nil
queue:nil
usingBlock:^(NSNotification *notification)
{
NSLog(#"%#", notification.name);
}];
Look at this: http://nshipster.com/nsnotification-and-nsnotificationcenter/
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.