How to ensure thread synchronization - iphone

I have a method myButtonAction which performs some heavy calculations, which I need to run on a background thread, while I am loading a view indicating "Task progress" in the main thread. As soon as the, background thread completes executing the method, I need to remove the "task progress" view and load another view in the main thread.
[self performSelectorInBackground:#selector(myButtonAction) withObject:nil];
[self performSelectorOnMainThread:#selector(LoadView) withObject:nil waitUntilDone:YES];
The problem I am facing is that, before myButtonAction completes execution, the LoadView completes its execution. How can I make sure that LoadView starts execution only after myButtonAction completes its execution.
Note: myButtonAction has its method definition in another class.

Use Grand Central Dispatch:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self myButtonAction];
dispatch_async(dispatch_get_main_queue(), ^{
[self LoadView];
});
});
Or, if you want to stay with performSelector methods:
[self performSelectorInBackground:#selector(loadViewAfterMyButtonAction) withObject:nil];
where
- (void)loadViewAfterMyButtonAction
{
[self myButtonAction];
[self performSelectorOnMainThread:#selector(LoadView) withObject:nil waitUntilDone:YES];
}

You need to do following -
[self performSelectorInBackground:#selector(myButtonAction) withObject:nil];
- (void)myButtonAction {
//Perform all the background task
//Now switch to main thread with all the updated data
[self performSelectorOnMainThread:#selector(LoadView) withObject:nil waitUntilDone:YES];
}
EDIT -
Then you can try -
[self performSelectorInBackground:#selector(buttonActionInBackground) withObject:nil];
- (void)buttonActionInBackground {
[self myButtonAction];
//Now switch to main thread with all the updated data
[self performSelectorOnMainThread:#selector(LoadView) withObject:nil waitUntilDone:YES];
}
Now you don't need to change myButtonAction.

I take it that this code gets called at the end of myButtonAction:
[self performSelectorOnMainThread:#selector(LoadView) withObject:nil waitUntilDone:YES];
now no wonder that LoadView finishes before myButtonAction finishes, because you said it to wait until it's done with "waitUntilDone:YES". Call it at the end with waitUntilDone:NO.
For simple solutions to this kind of problems take a look at putting selector calls into the main run loop using [self performSelector:#selector(sel) withObject:obj afterDelay:0.0] or NSTimer - for example if you want to wait until UI updates are finished.

I use a Semaphore Design Pattern for these purposes.

Related

Dismiss MBProgressHUD when delegate method fires

First time working with a HUD and I'm confused.
I setup the HUD like this in my viewDidLoad:
[MBProgressHUD showHUDAddedTo:self.view animated:YES];
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
[[[WSXmppUserManager shared] xmppStreamManager] connect];
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:self.view animated:YES];
});
});
The HUD doesn't show. I think the reason is as follows. The xmpp connect method fires off a connection request to the xmpp server and then it's done. So there is no activity to wait for as is.
However, the connection isn't established until the server responds and the following delegate method is fired:
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender
I want to wait for this and only then dismiss the HUD, but I'm at a loss as to how to do that. I'm missing something very simple.
You need to move this code
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:self.view animated:YES];
});
After your long running method has finished... that is, if this code is indeed returning immediately
[[WSXmppUserManager shared] xmppStreamManager] connect];
The hud is likely never going to display... as it gets told to display and then told to hide on the same run loop or perhaps one run loop right afterwards...
Why not put it at the end of this method if this indicates that a response has been received and the operation is completed?
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender
HUD =[[MBProgressHUD alloc] initWithWindow:self.view];
[HUD setDelegate:self];
[self.view addSubview:HUD];
[HUD showWhileExecuting:#selector(connectToServer)
onTarget:self
withObject:nil
animated:YES];
In the connectToServer
-(void)connectToServer
{
[[[WSXmppUserManager shared] xmppStreamManager] connect];
}
As soon as the connectToServer method comepletes it task in the background , a delegate of MBProgressHUD called hudWasHidden: is automatically called
-(void)hudWasHidden:(MBProgressHUD *)hud
{
//Further work after the background task completed
}

Showing MBProgressHUD during method duration

I have a method to get contacts from the address book and doing some stuff with them ("getContactInformation").
This process is a bit long (a few seconds) and after this process I show a new ViewController. To make it friendly to the user I would like to use MBProgressHUD to show an activity indicator at the beginning of the process and hide it at the end.
Which is the best way to do it? I've test this:
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.labelText = #"Retrieving information...";
[self getContactInformation];
[MBProgressHUD hideHUDForView:self.view animated:YES];
[self presentViewController:newController animated:NO completion:nil];
But it doesn't work (It doesn't show the indicator). Anyone can help me?
Thank you in advance
Keep a separate method for [self presentViewController:newController animated:NO completion:nil];. And try calling that method after some particular delay. That should be solving the problem.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
[self getContactInformation];
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:self.mapView animated:YES];
});
});
This will spawn a thread to run getContactInformation and won't block the main thread which is required to do UI changes (such as displaying the HUD).
I'm guessing the main thread is getting locked up when you get contact info and doesn't have time to update to show the HUD. Once the method is complete, it gets the main thread again to update the UI (removing the HUD).

Strange, IPhone performSelector on sub-thread doesn't work.

I want to do some action on a thread using the following API, so strange selector poiOneBoxSearch hasn't been invoked, why? Any mistake on the code? Thanks.
- (void)poiOneBoxSearch{
[self poiOneBoxSearcWithQueryString:#"coffee" isFinished:YES];
}
- (void)test1{
NSThread* thread = [[NSThread alloc] init];
[self performSelector:#selector(poiOneBoxSearch)
onThread:thread
withObject:nil
waitUntilDone:YES];
[thread release];
}
If You want use performSelector Method You should Read below Link
,I Think You missed SOmething
Please Goes Through This Link
If Not you may Use Below Code.
Try This
- (void)test1{
[NSThread detachNewThreadSelector:#selector(poiOneBoxSearch) toTarget:self withObject:nil];
}
Try this:
[self performSelectorInBackground:#selector(poiOneBoxSearch) withObject:nil waitUntilDone:YES];
[self performSelectorInBackground:#selector(poiOneBoxSearch) withObject:nil];
- (void) poiOneBoxSearch{
#autoreleasepool {
[self poiOneBoxSearcWithQueryString:#"coffee" isFinished:YES];
} }
The most important thing that you have to keep in mind is that since this method creates a thread on the given selector, the selector must have an autorelease pool just like any other thread in a reference-counted memory environment.

How to cancel or stop NSThread?

I'm doing an app that loads the contents of viewControllers using NSThread while is reading an XML file.
I have it done as follows:
-(void)viewDidAppear:(BOOL)animated
{
// Some code...
[NSThread detachNewThreadSelector:#selector(loadXML) toTarget:self withObject:nil];
[super viewDidAppear:YES];
}
-(void)loadXML{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Read XML, create objects...
[pool release];
}
My problem is that I don't know how to stop the NSThread if the user changes to another viewController while the NSThread is loading, doing that the app crashes.
I've tried to cancel or exit the NSThread as follows but without success:
-(void)viewsDidDisappear:(BOOL)animated{
[NSThread cancel];
// or [NSThread exit];
[super viewDidDisappear:YES];
}
Can anyone help? Thanks.
When you detach new thread, you can no more cancel or exit it from viewDidDisappear etc. These UI specific methods execute only on main thread so the exit/cancel applies to the main thread which is obviously wrong.
Instead of using the detach new thread method, declare NSThread variable in .h and initialize it using initWithTarget: selector: object: method and cancel it whenever/wherever you want to..
you can also use [NSThread exit]; method of NSThread.
It's better to let a thread end gracefully, i.e. reach its natural conclusion, if you can. It sounds like in your case you can afford to. Also be sure that you're updating the user interface from the main thread, not a secondary thread, as UIKit is not thread safe.
You wrote:
... the app stops responding while the thread finishes...
Once you flag a thread for cancelling or exit, you have to manually stop whatever the thread was called to do. An example:
....
- (void) doCalculation{
/* Do your calculation here */
}
- (void) calculationThreadEntry{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSUInteger counter = 0;
while ([[NSThread currentThread] isCancelled] == NO){
[self doCalculation];
counter++;
if (counter >= 1000){ break;
} }
[pool release]; }
application:(UIApplication *)application
- (BOOL)
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
/* Start the thread */
[NSThread detachNewThreadSelector:#selector(calculationThreadEntry)
toTarget:self withObject:nil];
// Override point for customization after application launch. [self.window makeKeyAndVisible];
return YES;
}
In this example, the loop is conditioned on the thread being in a non-cancelled state.

iphone: performSelector: withObject: afterDelay: does not work with a background thread?

I want to run a method in a background thread, the first method will run another method on the same (background) thread after some seconds. I wrote this:
- (IBAction)lauch:(id)sender
{
[self performSelectorInBackground:#selector(first) withObject:nil];
}
-(void) second {
printf("second\n");
}
-(void) first {
NSAutoreleasePool *apool = [[NSAutoreleasePool alloc] init];
printf("first\n");
[self performSelector:#selector(second) withObject:nil afterDelay:3];
printf("ok\n");
[apool release];
}
but the second method is never called, why? and, how may i accomplish my goal?
thanks
You have to have a running run loop for performSelector:withObject:afterDelay: to work.
Your code executes first and, when first exits, the thread is gone. You need to run a run loop.
Add:
[[NSRunLoop currentRunLoop] run];
To the end of first.