I can perfectly load my map view with annotations the first time. However, if i try to reload the map on a button click (after its already loaded), the user has to wait till the process completes.
This problem has arisen because on reload, the new annotations dont appear until the mapview is moved significantly, that's when the viewForAnnotation fires. I've seen two other questions similar to mine with solutions involving 'performSelectorInBackground' & 'performSelectorOnMainThread'. The former didnt work for me :( & the latter i dont want to do (though it's the only option that works) as i want the user to be able to interact with the map while the annotations load without blocking the main thread. I'm aware that such animations are best done on the main thread, so the question(s) 1. Is there no other way to do it than having the user wait till the map reloads? 2. Suggestions on the best way to do it?
Thanks in advance.
You can use a dispatch queue block to achieve this
here is the syntax
You can create your on private queue like this
dispatch_queue_t queue = dispatch_queue_create("com.MyApp.AppTask",NULL);
dispatch_queue_t main = dispatch_get_main_queue();
dispatch_async(queue,
^{
//do the fetching of data here(Don't do any UI Updates)
dispatch_async(main,
^{
// Do the UI Update here.
});
});
Apple has referred to this as recursive decomposition.
In this bit of code the computation is offloaded onto a background thread with dispatch_async() and
then dispatch_async() back into the main queue which will schedule our block to run with the updated data that we computed in the background thread.
Related
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.
App receives event from child thread, but we need to send it to main thread to do UI update issue, current now use method dispatch_async, dues to main thread is very busy, for some case it will cost two or three seconds to arrive at the destination, slow, try to improve, but so far no better method found, any idea or discussion will be appreciated, thanks in advance.
dispatch_async(dispatch_get_main_queue(), ^{ handleLoginOK(value); });
Look into -performSelectorOnMainThread:withObject:waitUntillDone: it may or may not be faster, I am not completely sure.
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.
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.
If I spawn a new thread, and then within it I push a new controller onto my UINavigationController, using code like this...
(a) not working
-(void)myCallbackInThread
{
// move on...
UIApplication* app = [UIApplication sharedApplication];
[app changeView];
}
then I find that the view appears, but does not respond to user input.
If I change the code like this
(b) working
-(void)myCallbackInThread
{
// move on...
UIApplication* app = [UIApplication sharedApplication];
[app performSelectorOnMainThread:#selector(moveToMain) withObject:nil waitUntilDone:FALSE];
}
Then everything works just fine.
Any hints as to why?
In your case, it really depends on what's happening in [app changeView], but the reason it stops responding is most likely that you have no run loop dispatching events on your new, secondary thread (more on this below). In general, however, it is a very bad idea to update the GUI from a secondary thread. As you've already discovered, all of these events should go through the main thread.
The main reason that your second example works and not your first is that UIApplication sets up and handles the run loop and event dispatcher for you on the main thread. So, when you call performSelectorInMainThread, the selector gets dispatched to the main run loop which is able to then handle your gui input and other events. The event dispatcher is also run and managed by UIApplication on the main thread.
So basically, don't perform any GUI management activities on a secondary thread. Dispatch those to the main thread. And if you need processing on a secondary thread (for things like timers or asynch calls, etc.) then you have to start and manage your own run loop on that thread (see NSRunLoop for more on managing your on run loop).
Just found this in the iPhone threading docs
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
it also has the advantage of
simplifying the logic for managing
your user interface.
I still don't see what would actually cause something to display but not be able to receive user input, but I'll follow that guideline in future.
As the documentation says, "If you’re not sure about a particular graphical operation, plan on doing it from your main thread."
A good rule of thumb to follow is that, if a class isn't explicitly documented as being thread-safe, then it's probably not. Additionally, code that's not documented as being thread-safe may not fail fast when used by multiple threads, but may simply exhibit undefined behavior, as you saw.
Almost none of the UI code in UIKit or AppKit is threadsafe. How it fails is irrelevent, because if you are worrying about how it fails you are doing something that is going to result in all sorts of weird bugs that will subtly change between different OS release anyway.
My best advice is to not use things from background threads unless the docs say it is safe.