How does Dispatch.main.async "update the UI"? - swift

I've been using Swift for a little while and GCD still confuses me a bit.
I've read:
https://www.raywenderlich.com/60749/grand-central-dispatch-in-depth-part-1
As well as the Apple docs on dispatch:
https://developer.apple.com/documentation/dispatch
I understand the overall concept that GCD allows multiple tasks to be run on different threads (I think that's right).
What I don't quite understand is how Dispatch.main.async "updates the UI".
For example if I make a call to an api somewhere and data is returned - say it takes 5 seconds to return all the data, then how does using Dispatch.main.async help with updating the UI? How does Dispatch.main.async know what UI to update?
And i still don't quite get the place of GCD and why instead can't some kind of observer or a delegate or a closure be used that is called when all the data is loaded?
And re: "updating the UI" with GCD if I'm making an api call but not using the data immediately eg. just storing the data in an array until I decide to use it is there then any need to use Dispatch.main.async?
And I've been using firebase/firestore as a db for a little while now. Firebase has it's own listeners and runs asynchronously. I still can't get a great answer re: the best way to handle the asynchronous return from firebase in iOS/Swift. For example when my app loads if I go to firebase to get data to populate a tableviewcontroller what is the best way to know when all the data has returned? I've been using a delegate for this but was wondering if and how Dispatch.main.async might be used.

Dispatch.main.async does not update the UI. The story goes into a different direction: If you want to update the UI, you must do so from the main thread. If you're current code is not running on the main thread, Dispatch.main.async is the most convenient way to have some code run on the main thread.
It's an old restrictions that affects most operating systems: UI related actions such as changing elements in the UI must only be called from a specific thread, usually the so called main thread.
In many cases that's not a problem since your UI related code usually acts when triggered by some UI event (user clicking or tapping, key pressed etc.). These event callback happen on the main thread. So there is no threading issue.
With GCD, you can run long-running tasks on separate threads so the tasks doesn't slow down or even block the UI. So when these tasks are finished and you want to update the UI (e.g. to display the result), you must do so on the main thread. With Dispatch.main.async you can ask GCD to run a piece of code on the main thread. GCD doesn't know about the UI. Your code must know what to update. GCD just runs your code on the desired thread.
If at the end of your tasks there is nothing to display or otherwise update in the UI, then you don't need to call Dispatch.main.async.
Update re Firebase
The Firebase Database client performs all network and disk operations in separate background thread off the main thread.
The Firebase Database client invokes all callbacks to your code on the main thread.
So no need to call Dispatch.main.async in the Firebase callbacks. You are already on the main thread.

FYI the reason that all of the UI code needs to go on the main thread is because drawing is a (relatively in CPU time) long and expensive process involving many data structures and millions of pixels. The graphics code essentially needs to lock a copy of all of the UI resources when its doing a frame update, so you cannot edit these in the middle of a draw, otherwise you would have wierd artifacts if you went and changed things half way through when the system is rendering those objects. Since all the drawing code is on the main thread, this lets he system block main until its done rendering, so none of your changes get processed until the current frame is done. Also since some of the drawing is cached (basically rendered to texture until you call something like setNeedsDisplay or setNeedsLayout) if you try to update something from a background thread its entirely possible that it just won't show up and will lead to inconsistent state, which is why you aren't supposed to call any UI code on the background threads.

Related

Why must UIKit operations be performed on the main thread?

I am trying to understand why UI operations can't be performed using multiple threads. Is this also a requirement in other frameworks like OpenGL or cocos2d?
How about other languages like C# and javascript? I tried looking in google but people mention something about POSIX threads which I don't understand.
In Cocoa Touch, the UIApplication i.e. the instance of your application is attached to the main thread because this thread is created by UIApplicatioMain(), the entry point function of Cocoa Touch. It sets up main event loop, including the application’s run loop, and begins processing events. Application's main event loop receives all the UI events i.e. touch, gestures etc.
From docs UIApplicationMain(),
This function instantiates the application object from the principal class and instantiates the delegate (if any) from the given class and sets the delegate for the application. It also sets up the main event loop, including the application’s run loop, and begins processing events. If the application’s Info.plist file specifies a main nib file to be loaded, by including the NSMainNibFile key and a valid nib file name for the value, this function loads that nib file.
These application UI events are further forwarded to UIResponder's following the chain of responders usually like UIApplication->UIWindow->UIViewController->UIView->subviews(UIButton,etc.)
Responders handle events like button press, tap, pinch zoom, swipe etc. which get translated as change in the UI. Hence as you can see these chain of events occur on main thread which is why UIKit, the framework which contains the responders should operate on main thread.
From docs again UIKit,
For the most part, UIKit classes should be used only from an application’s main thread. This is particularly true for classes derived from UIResponder or that involve manipulating your application’s user interface in any way.
EDIT
Why drawRect needs to be on main thread?
drawRect: is called by UIKit as part of UIView's lifecycle. So drawRect: is bound to main thread. Drawing in this way is expensive because it is done using the CPU on the main thread. The hardware accelerate graphics is provided by using the CALayer technique (Core Animation).
CALayer on the other hand acts as a backing store for the view. The view will then just display cached bitmap of its current state. Any change to the view properties will result in changes in the backing store which get performed by GPU on the backed copy. However, the view still needs to provide the initial content and periodically update view. I have not really worked on OpenGL but I think it also uses layers(I could be wrong).
I have tried to answer this to the best of my knowledge. Hope that helps!
from : https://www.objc.io/issues/2-concurrency/thread-safe-class-design/
It’s a conscious design decision from Apple’s side to not have UIKit be thread-safe. Making it thread-safe wouldn’t buy you much in terms of performance; it would in fact make many things slower. And the fact that UIKit is tied to the main thread makes it very easy to write concurrent programs and use UIKit. All you have to do is make sure that calls into UIKit are always made on the main thread.
So according to this the fact that UIKit objects must be accessed on the main thread is a design decision by apple to favor performance.
C# behaves the same (see eg here: Keep the UI thread responsive). UI updates have to be done in the UI thread - most other things should be done in the background hen possible.
If that wouldn't be the case there would probably be a synchronization hell between all updates that have to be done in the UI ...
Every system, every library, needs to be concerned about thread safety and must do things to ensure thread safety, while at the same time looking after correctness and performance as well.
In the case of the iOS and MacOS X user interface, the decision was made to make the UI thread safe by only allowing UI methods to be called and executed on the main thread. And that's it.
Since there are lots of complicated things going on that would need at least serialisation to prevent total chaos from happening, I don't see very much gained from allowing UI on a background thread.
Because you want the user to be able to see the UI changes as they happen. If you were to be able to perform UI changes in a background thread and display them when complete, it would seem the app doesn't behave right.
All non-UI operations (or at least the ones that are very costly, like downloading stuff or making database queries) should take place on a background thread, whereas all UI changes must always happen on the main thread to provide as smooth of a user experience possible.
I don't know what it's like in C# for Windows Phone apps, but I would expect it to be the same. On Android the system won't even let you do things like downloading on the main thread, making you create a background thread directly.
As a rule of thumb - when you think main thread, think "what the user sees".

run a process in the background while user can still use the UI

I am attempting to run a database fetch process in the background without locking the user interface.
Currently I have a button that does this, but I would like it to be automatic so that it can get more results as user is browsing current results.
Here is the code that the button does, I would like to make this automatic and not lock the UI. Also if there is a way to pause the process, but continue where it left off if user goes to another screen that would also be very useful.
Thanks in advance!
-(IBAction)continueUpdatingResultsButtonPressed:(UIButton*)sender{
[findMoreButton removeFromSuperview];
[self continueFindingMoreRecipes]; //(do this in background without locking screen)
[self loadRefreshButton];//At completion load this button (a blinking button) to refresh the cells with new results
}
A typical pattern you can use is something like this:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// perform data processing here (done in the background)
dispatch_async(dispatch_get_main_queue(), ^{
// update user interface here (done on the main thread)
});
});
You could do batch requests where you cache the next X amount of answers every time your UI got with in Y of the current end. Depending on what you are using a lot of databases have protocols that can help you batch cache easily. Good luck!
Use grand central dispatch. Create a new queue, dispatch it with a block and when you need to update, call dispatch get main queue. There is no way to pause this once the queue has been dispatched though. Maybe load recipes into intermediary then update as needed.
Look for some gcd tutorials there are a few decent ones there.
Would give you more code but I'm typing on iPhone.
It strikes me (especially since you are, by your own admission, "very new to programming") that it might not be prudent to pursue GCD to prefetch data in a background queue, make sure you coordinate the background queue's database operations with the foreground's database operations (possibly via something like FMDB's FMDatabaseQueue or something equivalent), gracefully handle pausing this operation as you go to other screens (as you indicated in your question) and making sure you don't cause retain cycles in the process.
To make matters worse, I gather that this is all in pursuit of a possibly questionable goal, if I understand you correctly, to retrieve 10,000 recipes in the background. Don't get me wrong. You certainly can design all of the background operations like we've outlined, but I think you should stop and ask yourself whether that's the right design for your business problem.
I might suggest an infinitely easier solution. Just load your tableview with the first x recipes, and as the user scrolls down, when you start to approach the end of the tableview, detect that fact and retrieve the next x records and add them to the tableview. But any user interface that is expecting the user to flip through 10,000 entries doesn't pass the smell test. When I think of a database with 10,000 entries, I think of something more like a imdb/wikipedia/google/facebook-like user interface rather than a contacts-style user interface.
Regardless, you almost certainly don't want your app just endlessly retrieving recipes in the background. You can solve your UI performance issue, but maybe replace it with memory management issues. And you're going to design a complicated system architecture when it's not entirely clear whether your recipe app requires that.

Objective C - Single Background Thread

I want to run a single background thread for an iPhone application which is available in background all the time and gets executed when specific event fires and go to wait for specific event to fire to start its execution again. During the execution of thread if specific event is fired again then thread should restart its work.
I am working on a custom map application. On TouchesMoved event, I need to load the map image tiles according to the positions moved in a background thread. The problem is that when I move the map with speed the touchesMoved event is fired the previous thread has not finished its work and new thread is started. It causes thread safety issue and my application is crashed.
So I am thinking of a solution to have a single thread all the time available and starts its work when touchesMoved is fired if touchesMoved is fired again it should restart its work instead of starting a new thread. I think it will prevent the thread safety issue.
Please help
Firstly I'd echo the use of NSOperation and NSOperationQueue. You could fall-back to using NSThread directly, but the point of NSOperation is that it hides threading from you, leaving you to concentrate on the processing you need to do. Try firing NSOperation requests as and when required, see what the performance is like in your use-case; even if these operations get data in an async manner, it should provide you with a cleaner solution with good performance, plus future proof.
I've successfully used NSInvocationOperation to fire requests as often as required, and it sounds like the sort-of requirements and behaviour you're after. I would suggest more generally that you experiment with these in a test project; here you can test performance.
The following weblog's helped me start playing with NSOperation:
http://www.dribin.org/dave/blog/archives/2009/09/13/snowy_concurrent_operations/
http://www.cimgf.com/2008/02/16/cocoa-tutorial-nsoperation-and-nsoperationqueue/
As always, the Apple Threading Programming Guide is a key read, to figure out which way to go depending on needs.
This sounds like an ideal job for an NSOperationQueue. Have a read of the operation queue section of the concurrency guide.
Essentially, you create an NSOperation object for each map tile load and place them on a queue that only allows them to execute one at a time.
Put a run loop in your background compute thread. Then use an NSOperation queue to manage sending messages to it. The queue plus the run loop will serialize all the work requests for you.

Avoiding infinite recursion synching between multiple NSManagedObjectContexts

The setup:
I have two managed contexts setup (on an iPhone application). A main context that I use for most queries and a background context I use for long running operations that I want to happen in the background.
I've setup notifications for NSManagedObjectContextDidSaveNotification against each managed object context. In response to the notification, I call mergeChangesFromContextDidSaveNotification to sync up. It is also a requirement that any time things change on the main context, I need to run some jobs on the background context, as the state of things has now changed.
This works fine, as long as only write in the one of the contexts. In this case, writing on the main context. However, if I write on the background context, this causes an infinite loop. Saving on the background context triggers the notification to the main context, which in turn merges the changes and fires its own notification, picked up by background context. This triggers the background context to perform its background jobs, which (if they write anything) start the cycle again.
It seems that either this is the wrong setup, or I need a way to decouple the "start processing background jobs any time something changes on the main context" from the change notification, or I need to make the background context read-only.
Ideas? Unlike a typical "second-context-is-for-importing" scenario, I believe that I need/want my background context to remain up-to-date with respect to the main context, so that I get the proper results from my background job.
Sounds like a design issue. While you can probably get around it, you should consider avoiding having a "permanent" background context. I normally recommend standing up a context per operation (for which NSOperations work great for) and then throw them away when that one job is complete. This avoids the need to keep multiple contexts in sync as you only need to update the main context.
Why do you think you need a permanent background context? If the reason is performance have you analyzed it?
You could create an NSSet for both contexts which contain all NSNotifications you have received. Whenever you get another one, just check it against those before calling mergeChangesFromContextDidSaveNotification. Come to think of it, storing the last one for either context should be enough…

Iphone multithreading and AI

I have an ai loop that I would like to write for my iphone app. I am under the understanding that this loop will take along time to make calculations and block the main application.
I want to put it in a different thread and run everything off events.
For example I would have an event that would be called when the players turn started. The AI thread would listen to it and react.
What is the best way for my ai thread to communicate with the main thread? I use NSNotificationcenter a lot but I am reading strange stuff like it will not fire the event on the right thread?
What is the best way to communicate through event like actions with threads?
My recommendation would be to use an NSOperationQueue for your AI processing actions. As the user performs actions, create an NSOperation which handles the AI processing in response of that event and add it to the NSOperationQueue. If there are dependencies between these actions, or if you wish to split your processing up into smaller sub-actions, you can set these actions to process only when certain conditions are met.
Operations placed within an NSOperationQueue will run on a background thread, so they will not block the main thread. For UI updates or other actions that need to be taken on the main thread, I recommend using -performSelectorOnMainThread:withObject:waitUntilDone: to call a method within your operation that posts a notification via NSNotificationCenter's -postNotificationName:object:. Have your view controller or other controller respond to these notifications and do what they need to in response to your AI routine's results.
Instead of firing up a seperate thread manually, I would urge you to consider using NSOperation / NSOperationQueue. It is much easier to work with, far less prone to errors and well documented with tutorials and examples all over the web.
You can use the methods - performSelectorOnMainThread: to execute a given method on the main thread, and - performSelector:onThread: or - performSelectorInBackground: to execute a method on another thread. Using these methods you can fire events across threads.
Note though, to use - performSelector:onThread: a Run Loop must be running on the target thread, else you method will not be executed.
I fire the AI in a background thread with performSelectorInBackground and each time the UI needs to be notified about something (the AI resulting action, a progress bar showing the AI working, etc) I use performSelectorOnMainThread:.
Of course, remember to create a NSAutoReleasePool in the background thread.