Multi threading in iOS objective C - ios

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.

Related

Using while(true) statement a valid approach in iOS programming?

In objective C,
I am making my program to wait using while loop
doInitialize()
{
dispach_group_t loadDataGroup=dispatch_group_create();
dispatch_group_async(loadDataGroup,...get_global_queue(..),0),^{
renewauth();
}
dispatch_group_notify(loadDataGroup,...get_global_queue(..),0),^{
//Do other tasks once renew session has completed...
}
}
renewauth()
{
RenewAuthTokenInProgress=true;
startRenewThread();
**while (RenewAuthTokenInProgress);**
}
In turn startRenewThread() function also performs dispatch_async operation inside. So I have to make renewAuth() wait.
And async task in startRenewThread will update the bool variable once renewal is successful.
Is there any better approach of doing it other than dispatch_groups?
And is it good to make other threads wait with while (true) statement?
Manoj Kumar,
using a while loop to wait till the boolean variable change is not the correct approach to solve the problem. Here are few of the issues with this method
Your CPU is un-necessarily burdened with checking the variable regularly.
This will clearly show that developer isn't much equipted with basic skills of coding and features available with language.
If for any reason your variable will never change then your CPU will never stop checking the value of bool in while loop and blocks the execution of further code on the same thread.
Here are few of the correct approach :
Blocks or closures : Make use of blocks to execute the code asynchronously when the RenewAuthToken is done.
Delegates : if blocks are harder to understand, Make use of delegates and trigger the delegate when you are done with RenewAuthToken.
Notifications : Add observer for notifications in classes which needs to respond when RenewAuthToken is done and throw notification from the asynctask and let the class to catch it execute the code.
Locks : If it is necessary to block the execution of the thread till the response comes use locks to control the thread execution rather than using while loop
EDIT
As pointed out by fogmeister in comments
If you block the main thread for too long with a while(true) loop then
the app will actually be terminated by the iOS Watchdog as it will
assume it has crashed
Please have a look at the link : understand iOS watchdog termination reasons provided by fogmeister
Hope it helps.
I believe what you need it's a semaphore like:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
__block BOOL done = FALSE;
while (true) {
[self someCompletionMethod completion:^(BOOL success) {
if(success) { // Stop condition
done = TRUE;
}
// do something
dispatch_semaphore_signal(sem); // This will let a new iteration
}];
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
if(done) {
dispatch_async(dispatch_get_main_queue(), ^{
// Dispatch to main
NSLog(#"Done!");
break;
});
}
}
});
Semaphores are an old-school threading concept introduced to the world by the ever-so-humble Edsger W. Dijkstra. Semaphores are a complex topic because they build upon the intricacies of operating system functions.
You can see a tutorial here about semaphore and check it out more links: https://www.raywenderlich.com/63338/grand-central-dispatch-in-depth-part-2
I hope this can help you.
What you do is absolutely lethal. It blocks the running thread (presumably the main thread) so the UI is frozen. It runs one core at 100% load for no reason whatsoever which empties the battery rapidly and heats up the phone. This will get you some very, very unhappy customers or very, very happy ex-customers.
Anything like this has to run in the background: startRenewThread should trigger some action that sets RenewAuthTokenInProgress = NO and sets whether there is a new token or not, and then triggers further action.
This is an absolutely essential programming pattern on iOS (and Android as far as I know).

iOS - When To Call UI Changing Functions In The Main Thread

Every time I make an API call to my server to get data, I already know that I have to use the following block to execute UI changing commands because my API call executes in the background thread:
dispatch_async(dispatch_get_main_queue(), ^{
//do UI stuff
});
However, what if I have a function that does UI changing stuff outside of the API call block? For example:
-(void)doALotOfUIChanging
{
//do a lot of UI changing
}
In my API call block, do I need to call that UI changing function in the main thread like so?:
[apiObject getDataFromObject:my.Object successCallback:^(Array *data)
{
dispatch_async(dispatch_get_main_queue(), ^{
[self doALotOfUIChanging];
});
}
errorCallback:^(NSString *error)
{
NSLog(#"%#", error);
}];
Or do I not have to call it in the main thread since the function is already outside of the API call block like so?:
[apiObject getDataFromObject:my.Object successCallback:^(Array *data)
{
[self doALotOfUIChanging];
}
errorCallback:^(NSString *error)
{
NSLog(#"%#", error);
}];
I also have functions that perform segues to other view controllers, so I'm also wondering if I should call them in the main thread as well. I'm doing some code clean up and I don't want to have to constantly rewrite the dispatch_async function in situations that I might not have to, so any help or advice would be greatly appreciated. Thanks.
Short answer: Yes you should update your UI on main thread.
Threads and Your User Interface
If your application has a graphical user interface, it is recommended
that you receive user-related events and initiate interface updates
from your application’s main thread. This approach helps avoid
synchronization issues associated with handling user events and
drawing window content. Some frameworks, such as Cocoa, generally
require this behavior, but even for those that do not, keeping this
behavior on the main thread has the advantage of simplifying the logic
for managing your user interface.
There are a few notable exceptions where it is advantageous to perform
graphical operations from other threads. For example, you can use
secondary threads to create and process images and perform other
image-related calculations. Using secondary threads for these
operations can greatly increase performance. If you are not sure about
a particular graphical operation though, plan on doing it from your
main thread.
Reference:
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Multithreading/AboutThreads/AboutThreads.html
First of all you should never use self inside a block . You can use __weak yourClassName *weakSelf = self instead.
Regarding your problem, all UI changes should be done on main thread. So you need to do this :
[apiObject getDataFromObject:my.Object successCallback:^(Array *data)
{
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf doALotOfUIChanging];
});
}
Hope it helps. :)

Using Blocks and GCD to manage tasks

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.

MagicalRecord : how to perform background saves

I am building a news application, which basically fetches data from a distant server, using AFNetworkOperation (all operations are put in a NSOperationQueue in order to properly manage the synchronisation process and progress).
Each completion block of each AFNetworkOperation creates/deletes/updates core data entities.
At the whole end of the synchronisation process, in order to make all changes persistent, I perform a full save with following lines of code
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_async(queue, ^{
NSLog(#"saveInBackground : starting...");
[[NSManagedObjectContext defaultContext] saveToPersistentStoreWithCompletion:^(BOOL success, NSError *error) {
NSLog(#"saveInBackground : finished!");
}];
});
Unfortunately it always blocks the main thread during my save operation.
I might not use MagicalRecord properly and so any advice would be welcome.
After digging deeper inside MagicalRecord, it seems that my code is working well and does not block main thread at all.
My issue is not on MagicalRecord, but on the way I should use it on completion blocks of afnetworking operation.
I Will start a new discussion to provide full details on it.

Waiting on Multiple Async Tasks to Complete

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) {
}

Resources