Avoiding infinite recursion synching between multiple NSManagedObjectContexts - iphone

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…

Related

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

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.

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.

What is a runloop?

After reading the documentation for NSRunLoop I did not understand very much. I am spawning a secondary thread that has a NSTimer in it that launch every 1sec. Which update a label on the screen with the performSelectorOnMainThread..
However to get it to work I needed a runloop but I do not understand the concept of it?
Anyone who could try to explain it?
Thanks.
A run loop is effectively:
while(... get an event ...)
... handle event ...;
It runs on a thread; the main thread has the main event loop where user events are processed and most UI drawing, etc, occurs. The documentation explains it in detail.
However, in your case, you don't need a thread.
It sounds like all you are doing is periodically updating a label in the UI; something that isn't terribly compute intensive.
Just schedule your timer in the main thread and be done with it. No need for spinning up a thread, using performSelectorOnMainThread:, or incurring all the complexities of guaranteeing data coherency across threads.
Sorry -- didn't understand your question.
Internally, a run loop works by basically putting a flag in the run loop that says "after this amount of time elapses, fire the timer". No additional threads involved and, better yet, it isn't polling to check the time. Think of a run loop as effectively maintaining a timeline. It'll passively let time elapse until there is something of interest found on the timeline (all without polling -- polling sucks. to be avoided.)
It does mean, though, that a Timer will never be 100% accurate. As well, if you have a timer repeating every second, it'll drift over time.
Also; instead of directly triggering a drawing event. Your timer should invalidate the view that needs updating, then let the underlying objects deal with when it is best to actually update the screen.
This page explains it pretty well. FTA:
A run loop is essentially an event-processing loop running on a single thread. You register potential input sources on it, pointing it to the code that it should execute whenever input is available on those sources.

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.

Pause NSOperation

I have NSOperationQueue with some NSOperations in it (NSInvocationOperations, in particular). This operations do some calculations and change states of UI elements accordingly (of course, via performSelectorOnMainThread:...), often with use of animations.
My UI has UINavigationViewController and some buttons for navigation to another views. So user can leave current view, while calculations / animations are still in progress. And what I need is to stop this somehow until user comes back to current view.
The only solution I found is to create some thread-safe boolean flag - and to check it in all threads (something like: while !flag sleep_for_some_time;). Is there something better?
The question is a bit vague, so it's hard to say without knowing all of the code in play. With that said, I may approach the problem by:
Option 1. In your subclass of NSOperation, add your own atomic KVO property "isPaused". Within the operation itself, observe that property and handle accordingly if it ever changes.
Option 2. Are you ever suspending the Operation Queue itself? If so, consider observing that property from within your operations, and each one independently can take action if that value changes.
Option 3. Cancel all operations in the queue, and if the view appears again, just restart with new operations.
Overall, though, there is no magic bullet for pausing operations already in progress. You'll have to bake your own solution. The damage shouldn't be too bad though.