What am I trying to do:
- Synchronize the background with the web API without freezing the user interface. I use MagicalRecord , but it is not very specific to it.
- make sure i use contexts and so correctly
Actually my question is: do I understand correctly? Plus a couple of questions at the end.
So, the contexts provided by MagicalRecord are as follows:
- MR_rootSavingContext from PrivateQueueConcurrencyType , which is used to save data to storage, which is a slow process
- MR_defaultContext MainQueueConcurrencyType
- and for the background you would like to work with the context created by MR_context () , which is a child of the MR_defaultContext file and has PrivateQueueConcurrencyType
Now for saving in asynchronous mode we have two options:
- MR_saveToPersistentStoreWithCompletion () , which will save everything up to MR_rootSavingContext and write to disk
- MR_saveOnlySelfWithCompletion () , which will save only to the parent context (for example, MR_defaultContext for a context created using MR_context)
From there, I thought that I could do the following (let me call it "Attempt No. 1") without freezing the user interface:
let context = NSManagedObjectContext.MR_context() for i in 1...1_000 { let user = User.MR_createInContext(context) as User context.MR_saveOnlySelfWithCompletion(nil) }
But my guess was wrong. I looked at MR_saveOnlySelfWithCompletion and saw that it relies on
[self performBlock:saveBlock]
which according to apple docs
Asynchronously executes the given block in the queue of receivers.
So, I was a bit puzzled, as I expected this to not block the user interface because of this.
Then I tried (let me call this attempt number 2)
let context = NSManagedObjectContext.MR_context() for i in 1...1_000 { let user = User.MR_createInContext(context) as User dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { () -> Void in context.MR_saveOnlySelfWithCompletion(nil) } }
And it does the job, but it doesn't seem right.
Then I found something in the iOS 5.0 release notes
When sending messages to the context created using the association queue, you should use executeBlock: or performBlockAndWait: if your code is not already running on this queue (for the main type of the queue) or within the executeBlock ... invocation action (for the type of the private queue) . Within the blocks passed by these methods, you are free to use the NSManagedObjectContext methods.
So, I assume that:
- Attempt # 1 freezes the user interface because I actually call it from the main queue, and not within the execution area.
- Attempt # 2 works, but I create another thread while the context already has its own background thread
So of course , what I have to do is use saveWithBlock:
MagicalRecord.saveWithBlock { (localContext) -> Void in for i in 1...1_000 { User.MR_createInContext(context) } }
Performs an operation on the direct child of MR_rootSavingContext , which has the PrivateQueueConcurrencyType property. Thanks to rootContextChanged, any changes that go before MR_rootSavingContext will be available to MR_defaultContext.
So it seems that:
- MR_defaultContext is the ideal context when it comes to displaying data.
- editing is preferably done in the MR_context file (a child of the MR_defaultContext file)
- Long-term tasks, such as server synchronization, are preferably performed using saveWithBlock
What still fails is to work with MR_save [...] WithCompletion (). I would use it in the MR_context file, but since it blocked the main thread in my test cases, I do not see when this becomes relevant (or what I missed ...).
Thank you for your time:)