I have an app that allows users to obtain stream gauge info via web service. In rural areas, network connections may be slow or nonexistent. I would like to set a timeout on the fetch operation but am not really sure how to go about it. Here is the fetch operation:
[self.view addSubview:hud];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
stateGauges = [[GaugeList alloc] initWithStateIdentifier:stateIdentifier andType:nil];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
[hud removeFromSuperview];
});
});
If I wanted this operation to timeout (10 seconds), causing the activity indicator to disappear and return the app to normal functioning, how might I go about that?
Thanks!
blocks can't be canceled, they need to check themselves.
the bad & hard way (DYI)
you need a separate queue that just has a timeoutblock end where, on timeout you set a BOOL to isCancel. In the other block you check
the GOOD & easy way
use NSOperationQueue & NSOperation APIs -- :)
There, NSOperationQueue also won't kill long running operations automatically but the general infrastructure is in place and it is easier to accomplish
e.g. the operation could do it on its own... see AFNetworkingOperations for one way of doing it
Related
I have a doubt regarding multi threading in iOS objective C. I have never worked on threads..
In my app, I have a couple of tasks that need to run only in background so that the UI doesn't get freezed.
Following is the code snippet,
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
[self someFunctionCallinWebservice];
dispatch_async(dispatch_get_main_queue(), ^(void){
//UI Updates
});
});
In the above code, function : someFunctionCallinWebservice calls webservice for which I am using AFNetworking library. In the webservice if it is a success then I am saving the data locally. I am calling a function in success block to save the data sent from server like below,
[manager POST:url parameters:parameter success:^(AFHTTPRequestOperation *operation, id responseObject){
[self functionToSaveData:someArray];
}
Here the someFunctionCallinWebservice is running in background but [self functionToSaveData:someArray] runs in foreground. Should I have this functionToSaveData also in background thread?
I mean if I am calling a function in background then all related functionalities of that function like, calling server, getting the data and saving it must also fall in background thread right? Why should I create another thread again?
Please help...
Yes, u can call functionToSaveData function in background thread it will not create any issue but if u want to do any UI updates (like :-> reload tableView, show or hide some views) at that time u must do it on main thread otherwise it will not do any effect on your UI.
dispatch_async(dispatch_get_main_queue(),^{
//Do any UI updates here
});
Edit: Swift 4
DispatchQueue.main.async {
//Do any UI updates here
}
Multi-threading is a large and difficult subject, for which iOS has different types of supports. I suggest you read Apple's Threading Programming Guide to start with.
For the type of action that you seem to be doing (fetching data from the internet), I suggest you use the iOS asynchronous APIs, such as URLSession, which remove the need to do anything with multi-threading yourself.
The answer to your concrete question depends on whether your POST:parameters:success: operation is a synchronous or an asynchronous operation, and it depends on what the functionToSaveData: actually does.
Assuming that functionToSaveData: is intended to share the data with the rest of your app, it would be best to do it on the main thread, to avoid synchronisation problems.
I want to call a web service to upload some image data to the server. I have to send the data 5 times to the server. This piece of code is written in a function which is called after 10 seconds duration by a timer. Now the problem is that the response of Web service might be late and second call to web service might initiate. I want to keep them in queue so that when one finishes other is called. I think I am not going in right way. I just want to maintain a queue in which I can call the web service multiple times and make different async calls to the server. Basically how I could call multiple async tasks.
Any help will be appreciated.
dispatch_queue_t myQueue;
myQueue = dispatch_queue_create("My Queue",NULL);
dispatch_async(myQueue, ^{
[self uploadDataToServer];
dispatch_async(dispatch_get_main_queue(), ^{
// Update the UI
});
});
A simple way is to keep a counter and recurse. It looks like your uploadToServer is a blocking call so e.g.
- (void)uploadDataToServerAndRepeat:(NSUInteger)repeatCount {
if(repeatCount)
dispatch_async(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_NORMAL, 0),
^{
[self uploadDataToServer];
[self uploadDataToServerAndRepeat:repeatCount - 1];
// dispatch async to main queue for UI update, too
});
}
// ... and, to start things off ...
[self uploadDataToServerAndRepeat:5];
Added a sample project. Maybe there was any other way for it but you can simply use TaskQueue.m class in sample process. You can modify it if you wish. https://github.com/kocakmstf/AsyncTaskQueue
I have designed an application which sometimes needs to make intensive computing (inside a loop), that loads cpu heavily during few tenths of seconds. I launch a UIAlertView to display a message, something like: "please wait for few seconds".
My problem is that during this period the App is not responsive at all, and the UIAlertView itself cannot be dismissed by user. This is not a major issue but not fair for the user. But it could become a real problem if I was to implement some kind of cancel button.
How can I solve this ? For example is there some sleep command that I could use inside my computing loop when detecting too much cpu load ?
Thanks.
You shouldn't perform CPU intensive operations on the main thread - as it will impact the app responsiveness as you have seen. You can use a dispatch queue to perform the task on another thread.
There is more detail in the Apple guide I linked to, but in general you can use something like -
dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(aQueue, ^{
[self performIntensiveTask];
});
If necessary, you may need to know when your intensive task has completed. You could use an NSNotification to do this or you could just update your UI elements - if you choose the second be aware that you should only update UI elements on the main thread so you would have -
dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(aQueue, ^{
[self performIntensiveTask];
dispatch_async(dispatch_get_main_queue(), ^{
[self updateUI];
});
});
I'm learning iOS and when it comes to GCD, it's confusing.
Let's get it out of the way, I'm writing a small program that fetch data from the internet.
Here is my viewcontroller
NSMutableArray dataArray = [NSMutableArray array];
[querysomethingwithblock:(^ {
//do some stuff here
[otherquerywithblock:( ^ {
//do some stuff here
// Here I got the data from internet
// Do loop action
[dataArray addObject:data];
})];
})];
// here I want to perform some actions only after get data from internet
[self performAction:dataArray];
How can I achieve this purpose. In practical, [self performAction:dataArray] always get fired before I get the data. I tried to play with GCD but no luck.
Here is some patterns I've tried so far
dispatch_async(queue, ^{
// Do query stuff here
dispatch_async(dispatch_get_mainqueue(), ^{
//perform action here
});
{;
Or using dispatch_group_async, dispatch_group_wait, dispatch_group_notify
The only way I can handle right now is to use dispatch_after but the point is the downloading time is variable, it's not good practice to have a specific time here
Thank you so much for any advice.
The part of code called Do query stuff here i assume is async already, why put it inside a dispatch_queue then?
If instead you manage to do a synchronous query, your code (the second snippet) would work, as the dispatch to the main queue would be executed only after the query finished.
If you don't have an option to execute the query in a synchronous manner, then you need some mechanism to register either a block or a callback to be executed when the download is finished.
At the end of the day, it all depends on what kind of query you have in there and what methods it offers for you to register an action to be performed when the download is finished.
I like to show a loading message when the app is fetching news and videos. Currently, I have the following code:
[MBProgressHUD showHUDAddedTo:self.view animated:YES];
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
[self loadVersion];
[self loadFeaturedNews];
[self loadFeaturedVideo];
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:self.view animated:YES];
[self dismissViewControllerAnimated:NO completion:nil];
});
});
I would like to dismiss the controller and hide the progress view only when all the tasks (news, videos) are loaded.
If you're using AFNetworking, you might want to look at enqueueBatchOfHTTPRequestOperations. I'd refer you to the AFNetworking FAQ:
How can I wait for a group of requests to finish before processing them?
Use [enqueueBatchOfHTTPRequestOperationsWithRequests] or [enqueueBatchOfHTTPRequestOperations] to batch a series of requests together, specifying a callback for when all of the requests have finished. As mentioned in the question about waiting for completion blocks to finish, you may not want to set completion blocks on each individual operation, and instead access the response object properties directly in the batch completion block.
I gather that loadVersion, loadFeaturedNews, and loadFeaturedVideo methods are each asynchronously downloading content. If you were using NSURLConnection, I would suggest changing them to operate synchronously. The specifics of the solution will vary depending upon how you're currently downloading stuff in those routines e.g. if you're using NSURLConnection method, initWithRequest, you could use sendSynchronousRequest.
Personally, I'd be inclined to combine that with doing the requests concurrently with dispatch groups or NSOperationQueue dependencies, but first focus on getting them to run synchronously. I'd actually use NSOperationQueue so I could easily cap the number of concurrent operations to four, or something reasonable like that.
By the way, I'm not suggesting you change the code in your question at all. Keep that dispatch_async. I'm suggesting you fix the loadVersion, loadFeaturedNews, and loadFeaturedVideo methods, themselves, to operate synchronously.
Then use completion handler for this. in that you can write your code which is to be executed after completion
completion:^ (BOOL finished) {
}