In my application i am using back ground thread for hitting multiple service and perform operation with core data. I have used main thread for back ground process ,Its working fine.
Here is my code
dispatch_queue_t main = dispatch_get_main_queue();
dispatch_async(main,
^{
[self backGroundCall];
});
-(void)backGroundCall
{
NSLog(#"Done");
if([CacheManager refreshDBforFirstTimeUseWithDelegate:self])
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"IsDBInitialized"];
ContainerViewController *containerViewControllerInstance = [ContainerViewController getContainerInstance];
[containerViewControllerInstance setUserId:_userID];
[progressView setHidden:YES];
[self.view setUserInteractionEnabled:YES];
[self.navigationController setDelegate:containerViewControllerInstance];
[self.navigationController pushViewController:containerViewControllerInstance animated:YES];
}
}
once i initialize the data base , i need to navigate to the container view.During the initialization i will display one progress bar. That is working fine, when the entire background process is completed(app is in minimized state). During the background process if i come to the foreground progress bar is not showing at that time black screen is display instead of progress view . After the completion of the main threat container view all not display[if i comes to foreground of main thread process].
i need to show the progress bar, if i come back to the app in the middle of the main thread process. Please guide me to fix this issue.
Thanks.
dispatch_queue_t main = dispatch_get_main_queue();
dispatch_async(main,
^{
[self backGroundCall];
});
This is a bit misleading... You call the method backGroundCall, but you are actually doing this on the main thread. If you want to make some operation on a working thread, you can do this:
// Declare the queue
dispatch_queue_t workingQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(workingQueue,
^{
// My background job
dispatch_async(dispatch_get_main_queue(),
^{
// Update the UI
}
);
});
Related
Ok. So i am trying to use GCD To Handle all the heavy loading before transition to next view controller. I am opening large archive Files and extracting them which takes some time.
The entire Process is like this:
Click a UICollectionViewCell>Display activity indicator>Let GCD take care of heavy loading>call transition selector using performSelector: onThread:.....
The problem is when i use mainThread, the transition occurs too fast and all the heaving loading don't come to effect until after some time and the transition looks awful and while using currentThread, well it just takes so much time, it seems plain awful of a app.
-(void)someMethod
{
//activity Indicator before transition begins
UIActivityIndicatorView *activity=[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[activity setFrame:self.view.bounds];
[self.view addSubview:activity];
[self.view bringSubviewToFront:activity];
activity.hidesWhenStopped=YES;
[activity startAnimating];
dispatch_queue_t transitionQueue;
transitionQueue = dispatch_queue_create("com.app.transitionQueue", NULL);
dispatch_async(transitionQueue,^{
//heavy lifting code
viewerPVC=.....
dispatch_async(dispatch_get_main_queue(),^{
[activity stopAnimating];
[self transitionToMangaViewer:mReaderPVC];
});
};
}
-(void)transitionToViewer:(ViewerPVC*)viewerPVC
{
[self.navigationController pushViewController:mReaderPVC animated:YES];
}
So Tried The First Suggestion, but the transition still seems buggy since the CollectionViewController still remain On Background For Some Time after the transition
you shouldnt need to use NSThread when you are using gcd, try something like this instead
dispatch_async(transitionQueue,^{
//heavy lifting code
viewerPVC=..... //this should block here otherwise will not work
dispatch_async(dispatch_get_main_queue(), ^{
[activity stopAnimating];
[self transitionToAnotherViewer:viewerPVC];
});
});
UI Update should be done on Main Thread, no need to create New Thread for performing UI Transition. Try below code:
dispatch_async(transitionQueue,^{
//heavy lifting code
viewerPVC=.....
dispatch_async(dispatch_get_main_queue(), ^{
[activity stopAnimating];
[self performSelector:#selector(transitionToAnotherViewer:) withObject:viewerPVC waitUntilDone:YES];
});
};
Adding the following code to a new project cause a flash of a black screen. It seems to be caused by the fade out animation of the splash screen. Unfortunately, the long process must be on main thread. It is possible to avoid it by delaying it but it is unreliable and lengthens the loading process.
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
// A spinner is shown
// This most probably will not cause a black screen
// dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// [self loadSomething];
// });
// This will not cause a black screen but not suitable for my use
// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// [self loadSomething];
// });
dispatch_async(dispatch_get_main_queue(), ^{
[self loadSomething];
});
}
- (void)loadSomething
{
NSLog(#"start long process");
NSDate *start = [NSDate date];
while (-[start timeIntervalSinceNow] < 5) {
}
}
Consider why it must be on the main thread and come up with a way to move this processing or minimise it.
You should create a root view controller which displays the app default image and have that view controller displayed (probably with some animation or progress indication) until your processing has completed.
If you're already on your main thread, then there is no need of getting the main thread asynchronously.
dispatch_async(dispatch_get_main_queue(), ^{
});
The above code should be removed.
In case you want to do something or load something, then you should use
performSelectorInBackground
Hope this helps...
I've created an iPad App which internally load a large amount of media, which can freeze the UI for a few seconds (especially on older iPads). I'm exploring async and adding a spinner, but it I haven't been able to identify the right spot for spinner to start prior to new ViewController opening. Any help is appreaciated.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
[activityIndicatorObject startAnimating];
dispatch_queue_t downloadQueue = dispatch_queue_create("downloader", NULL);
dispatch_async(downloadQueue, ^{
understandingViewController *destViewController = segue.destinationViewController;
destViewController.itemNumber = num1;
destViewController.selectedItem = num1;
dispatch_async(dispatch_get_main_queue(), ^{
[activityIndicatorObject stopAnimating];
});
});
}
Use ViewDidLoad in the destinationViewController to add activityIndicator and startAnimating.
and in the ViewDidLoad , load your data in a background thread
so something like this
- (void)viewDidLoad {
[super viewDidLoad];
// add activity indicator
// start animating
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);
dispatch_async(queue, ^{
// Perform non main thread operation
// load data
dispatch_sync(dispatch_get_main_queue(), ^{
// perform main thread operation
});
});
}
You sound confused. View controllers are not "in the background". CODE runs in the background. You should not be doing async calls in prepareForSegue.
I would suggest skipping calls to dispatch_async() entirely. Instead, look at using NSURLConnection's sendAsynchronousRequest:queue:completionHandler: method. That will let you submit a connection request (pass in the main queue for the the queue parameter) and a block of code that you want to execute once the connection is complete. The system handles doing the download asynchronously and notifies you when it's done.
I'm using the MBProgressHUD library in my app, but there are times that the progress hud doesn't even show when i query extensive amount of data, or show right after the processing of data is finished (by that time i don't need the hud to be displayed anymore).
In another post i found out that sometimes UI run cycles are so busy that they don't get to refresh completely, so i used a solution that partially solved my problem: Now every request rises the HUD but pretty much half the times the app crashes. Why? That's where I need some help.
I have a table view, in the delegate method didSelectRowAtIndexPath i have this code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[NSThread detachNewThreadSelector:#selector(showHUD) toTarget:self withObject:nil];
...
}
Then, I have this method:
- (void)showHUD {
#autoreleasepool {
[HUD show:YES];
}
}
At some other point I just call:
[HUD hide:YES];
And well, when it works it works, hud shows, stays and then disappear as expected, and sometimes it just crashes the application. The error: EXC_BAD_ACCESS . Why?
By the way, the HUD object is already allocated in the viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
...
// Allocating HUD
HUD = [[MBProgressHUD alloc] initWithView:self.navigationController.view];
[self.navigationController.view addSubview:HUD];
HUD.labelText = #"Checking";
HUD.detailsLabelText = #"Products";
HUD.dimBackground = YES;
}
You need to perform your processing on another thread, otherwise the processing is blocking MBProgressHud drawing until it completes, at which point MBProgressHud is hidden again.
NSThread is a bit too low-level for just offloading processing. I'd suggest either Grand Central Dispatch or NSOperationQueue.
http://jeffreysambells.com/2013/03/01/asynchronous-operations-in-ios-with-grand-central-dispatch
http://www.raywenderlich.com/19788/how-to-use-nsoperations-and-nsoperationqueues
/* Prepare the UI before the processing starts (i.e. show MBProgressHud) */
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
/* Processing here */
dispatch_async(dispatch_get_main_queue(), ^{
/* Update the UI here (i.e. hide MBProgressHud, etc..) */
});
});
This snippet will let you do any UI work on the main thread, before dispatching the processing to another thread. It then returns to the main thread once the processing is done, to allow you to update the UI.
I'm trying to update my textView on screen before it starts downloading data. Right now, it only updates the view after all of the downloads are complete. How can I do it before or in between the downloads?
Edit: I want the self.textView.text = #"Connection is good, start syncing..."; to update the UI before the downloading starts. But right now, it only updates after the download finishes.
Here is what the code looks like.
if ([self.webApp oAuthTokenIsValid:&error responseError:&responseError]) {
self.textView.text = #"Connection is good, start syncing...";
[self.textView setNeedsDisplay];
[self performSelectorInBackground:#selector(downloadCustomers:) withObject:error];
}
I'm new to this and have yet to learn how threads work, but from what I read, the downloadCustomers function should be using a background thread leaving the main thread to update the UI.
if ([self.webApp oAuthTokenIsValid:&error responseError:&responseError]) {
self.textView.text = #"Connection is good, start syncing...";
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self downloadCustomers];
dispatch_async(dispatch_get_main_queue(), ^{
//Do whatever you want when your download is finished, maybe self.textView.text = #"syncing finished"
});
});
}
The pattern here is to initialize your download on background thread and then call back to main thread for UI update.
Below is an example using GCD. The advantage of GCD version is that you can consider using whatever you do in -downloadCustomers, to insert in-line where you call it.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self downloadCustomers];
dispatch_async(dispatch_get_main_queue(), ^{
[self.textView setNeedsDisplay];
});
});