Restkit and deadlock - ios5

Currently I'm using Restkit to control all my (Core-) data in my app. I'm using it to keep in sync with the server using RKManagedObjectMapping and I use [myMyNSManagedObject createEntitity] together with [[RKObjectManager §sharedManager].objectStore save] to manually edit items within Grand Central Dispatch.
Is there any recommendation to do this in this or an other way? Because sometimes the app freezes in a deadlock executing this code of Restkit
+ (NSArray*)objectsWithFetchRequest:(NSFetchRequest*)fetchRequest {
NSError* error = nil;
NSArray* objects = [[self managedObjectContext] executeFetchRequest:fetchRequest error:&error];
if (objects == nil) {
RKLogError(#"Error: %#", [error localizedDescription]);
}
return objects;
}
with that
- (NSError*)save {
NSManagedObjectContext* moc = [self managedObjectContext];
NSError *error;
#try {
if (![moc save:&error]) {
if (self.delegate != nil && [self.delegate respondsToSelector:#selector(managedObjectStore:didFailToSaveContext:error:exception:)]) {
…
in parallel. Before I switched to Restkit I put a "context performBlockAndWait" around each entity-editing code and was on the safe side with no deadlocks. I have no other NSManagedObjectContext or something created by myself, all comes from Restkit.

In my case, the problem was that I was passing NSManagedObjects across thread boundaries, and using them on threads other than the ones on which they were fetched from their respective NSManagedObjectContext. It was really subtle in my case, as I knew that I wasn't supposed to do this, but did it accidentally anyways. Instead of passing the managed objects, I started passing the NSManagedObjectIDs (and then fetching from the local thread's MOC), and haven't encountered a deadlock since. I'd recommend you do a deep scan of your code to make sure you are only using managed objects in the threads that spawned them.

We've encountered this exact problem in our app. Basically, CoreData nested contexts are very buggy in iOS5, and thus don't work as advertised. This has many manifestations, but one of them is the problem described above, deadlocking a fetch request vs. a background operation. This is well documented, instructive quote:
NSFetchedResultsController deadlocks
You never want your application to deadlock. With NSFetchedResultsController and nested contexts, it’s pretty easy to do. Using the same UIManagedDocument setup described above, executing fetch requests in the private queue context while using NSFetchedResultsController with the main queue context will likely deadlock. If you start both at about the same time it happens with almost 100% consistency. NSFetchedResultsController is probably acquiring a lock that it shouldn’t be. This has been reported as fixed for an upcoming release of iOS.
This SO answer has a possible fix that keeps nested contexts. I've seen others like it, basically liberally applying -performAndWait: calls. We haven't tried that yet (iOS5 has single digit percentages in our user base). Otherwise, the only other "fix" we know right now is abandoning nested contexts for iOS5 (cf. this SO answer).
That CoreData continues to be fundamentally broken for multithreading (in iOS 5) is inexcusable on Apple's part. You can make a good case now that trusting Apple on CoreData (multithreading, iCloud) seems to be opening a bag of pain.

Related

Core Data merge behaviour

I tried to find an answer to this question, but I couldn't figure out a question from neither the doc nor StackOverflow. If there is already a question like this, I just didn't find it, so it will be very welcomed as the solution in case.
My situation is:
I have two core data entities, a User and a Driving Licence.
User <--- 1 to 1 ---> Driving Licence
I'm using Magical Record as an abstraction layer for the core data operations.
My User class (derived from NSManagedObject) exposes 2 methods.
One to access a singleton instance of the User (the only one used throughout the app):
+ (User *)currentUser {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if ([User MR_findFirst] == nil) {
User *user = [User MR_createEntity];
user.drivingLicence = [DrivingLicence MR_createEntity];
[[user managedObjectContext] MR_save];
}
});
return [User MR_findFirst];
}
And a method used to reset the user data (derived from NSManagedObject):
- (void)resetFields
{
self.name = nil;
self.surname = nil;
....
[self.drivingLicence MR_deleteEntity];
self.drivingLicence = [DrivingLicence MR_createEntity];
[self.managedObjectContext MR_save];
}
Sometimes, I would say quite randomly, the drivingLicence field happens to be null.
There may be occasions when the resetFields method is called by a background thread.
Could it be that, for the merge with the other contexts, tha sequence of instructions
[self.drivingLicence MR_deleteEntity];
self.drivingLicence = [DrivingLicence MR_createEntity];
can cause some confusion, bringing the drivingLicence to be just deleted at end?
Or what else could it be the reason for this unexpected null value?
When you use MR_createEntity, you are implicitly using the default context, accessed through [NSManagedObjectContext MR_defaultContext]. It is quite dangerous to do this unless you are ABSOLUTELY POSITIVE you are calling that from the main thread. In your examples, all this should work correctly if everything is called from the main thread AND your self.managedObjectContext instance variable is also pointing to the default context. Otherwise, you will need to be explicit about which contexts you are using. MagicalRecord provides these conventions for you by having an inContext: optional parameter at the end of every method that requires a context to work. Have a look at the MR_createInContext: method and be explicit with your context usage
Just hit this problem. Based on our discoveries, I wanted to add some comments to casademora's answer above:
It is important to remember that core data, as well as any of the MR_save methods, are not thread safe. We delegate our MagicalRecord actions to a queue to get around this issue. Specifically, it's important to remember not to do save actions (such as MR_saveToPersistentStoreAndWait) in multiple threads at the same time.
However the connection between MR_createEntity, MR_defaultContext, and the Main thread is more subtle. As of MagicalRecord version 2.3.x, I do not believe that MR_createEntity, with no arguments, defaults to MR_defaultContext. I believe it defaults to the MR_contextForCurrentThread. MR_contextForCurrentThread returns the default context if it's on the Main thread, but otherwise it does not. This is dangerous if you don't understand the consequences, as you could easily see what the original poster sees above (lost data).
In fact, this note indicates that you should use MR_createEntityInContext:localContext rather than MR_createEntity due to problems with MR_contextForCurrentThread when using GCD.
See the function definition in github for the logic where MR_createEntity uses MR_contextForCurrentThread.

Is AFNetworking with CoreData thread-safe?

I am running into intermittent, hard-to-reproduce errors on my iPhone app, so I am checking my assumptions around concurrency.
Running AFNetworking v0.10.x, I have the following network call:
[self postPath:#"/myEndPoint"
parameters:params
success:^(AFHTTPRequestOperation *request, id response)
{
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
// do stuff with object context here
[appDelegate.objectContext save];
}
]
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
// do other stuff with object context
[appDelegate.objectContext save];
In my AppDelegate:
-(NSManagedObjectContext*) objectContext
{
if(nil == _objectContext)
{
... set up sqlite persistent store coordinator and object model ...
_objectContext = [[NSManagedObjectContext alloc] init];
[_objectContext setPersistentStoreCoordinator:persistentStoreCoordinator];
[_objectContext setMergePolicy:NSOverwriteMergePolicy];
}
return _objectContext;
}
Is it possible, in this scenario, to end up with concurrency problems? Or, in other words, is AFNetworking's API thread-safe? I thought the NSOverwriteMergePolicy would cover me for conflicts, but crashing persists (albeit intermittently).
AFNetworking's callbacks are executed on the main thread. As a result, they are 'thread-safe', because there is only one thread that is interacting with CoreData. If you only have a single managed object things will be straightforward.
From Apple:
Tasks added to this queue are performed serially on the main thread itself. Therefore, you can use this queue as a synchronization point for work being done in other parts of your application.
There are still lots of considerations when using multi-threaded CoreData and multiple managed object contexts, and for those I refer you to rsswtmr's excellent answer, which doesn't correctly answer my question, but provides links to lots of good information.
You can't have multiple threads working on the same object context. Think through how Core Data might be part-way through changing/committing data while another change comes through on another thread. You need to create a separate context per thread and merge changes when it's safe/appropriate. The NSOverwriteMergePolicy will simply save you from having to manually handle conflicts at merge time.
Look here for a great explanation of threading Core Data access.

CoreData performance about context saving

I finished converting my app to use the CoreData layer for a small datawarehouse I want to use. I have some concerns about the performance and how to best use it. In particular:
I have a lot of runs where I read from disk attributes within files: each attribute should generate a new object, unless an object of that type and that value already exists. So, for each file I read, I: execute a fetch to check if that managed object already exists; if yes finish, otherwise I create the object, assign value and save context.
Currently, I save the context once for each time I create a new object, so it happens more or less ten times (for the ten attributes) for each file read (which can be hundreds). Would be better to reduce the context saving points, maybe once for file instead of once for attribute? I do not know the overhead of this operation so I don't know if is ok to do this so often, or how to find out the time spent on this (maybe with the instruments? Don't really know how).
There isn't any need to save after setting each attribute.
Normally, you only save a managed object when the code is done with it as saving resets the undo. In the set up you describe, you could safely generate hundreds of managed objects before saving them to permanent store. You can have a large number (thousands) of lightweight (text attributes) objects in memory without putting any strain on the iPhone.
The only problem on the iPhone is that you never know when the app will be suspended or shut down. This makes saves more common than on other platforms. However, not to the extent you now use.
Core Data Performance section of the guide might help you plan. Instruments allows you to see the details of Core Data performance.
However, I wouldn't do anything until you've tested the app with a great deal of data and found it slow. Premature optimization is the source of all evil. Don't waste time trying to prevent a problem you may not have.
To prevent a "sudden application stop" problem you can implement something like that method:
- (void)saveContext {
NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
if (managedObjectContext != nil) {
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
LogError(#"Unresolved error %#, %#", error, [error userInfo]);
// abort();
}
}
}
and use it inside two methods of your app delegate:
- (void)applicationWillTerminate:(UIApplication *)application;
and
- (void)applicationDidEnterBackground:(UIApplication *)application;
Thought it may not be the 100% solution, but in most of the cases it will do the work...

Cryptic error from Core Data: NSInvalidArgumentException, reason: referenceData64 only defined for abstract class

I'm doing an iPhone app that reads data from XML file, turn them into Core Data Managed Objects and save them.
The application is working fine, mostly, on smaller data set/XML that contains ~150 objects. I said mostly because 10% of the time, I'd get the following exception from CoreData while trying to save the context:
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '* -_referenceData64 only defined for abstract class. Define -[NSTemporaryObjectID_default _referenceData64]!'
On a bigger data set (~2000), this happens every time, but not on the same place. It could fail on the 137th record, 580th, or the very last one. I've tried moving the save point (per object, per 10 objects, save once all objects are alloc/init'ed) but I always hit the exception above.
I've googled the exception and saw someone having the same issues but didn't see any resolutions.
My next step was going to be simplifying the managed objects and relationships to a point where this error stops and build from there to isolate the issue. The last resort is to ditch Core Data and just directly store into sqllite.
Thanks for all your help!
I have the same issue. It works for smaller data sets, but for larger sets I get "_referenceData64 only defined for abstract class" errors. There's no abstract entities in my model.
EDIT:
I think I got this resolved. The issue in my case was a confusion on my part re threads. Here's the guidelines I followed to fix it:
I parse XML data in a thread. On launching said thread, create a new NSManagedObjectContext using the same persistent store coordinator as your main thread's NSManagedObjectContext.
Any new objects you make in your thread should be made for the thread's NSManagedObjectContext. If you have to copy over objects from the main thread's NSManagedObjectContext, copy over by ID. I.e.
NSManagedObjectID *objectID = [foo objectID];
FooClass *newFoo = [(FooClass*)[threadManagedObjectContext objectWithID:objectID] retain]
When finished parsing, you need to save the changes made to the thread's NSManagedObjectContext. You must lock the persistent store coordinator. I used the following (incomplete code):
`
- (void)onFinishParsing {
// lock the store we share with main thread's context
[persistentStoreCoordinator lock];
// save any changes, observe it so we can trigger merge with the actual context
#try {
[threadManagedObjectContext processPendingChanges];
}
#catch (NSException * e) {
DLog(#"%#", [e description]);
[persistentStoreCoordinator unlock];
}
#finally {
// pass
}
NSNotificationCenter *dnc = [NSNotificationCenter defaultCenter];
[dnc addObserver:self selector:#selector(threadControllerContextDidSave:) name:NSManagedObjectContextDidSaveNotification object:threadManagedObjectContext];
#try {
NSError *error;
if (![threadManagedObjectContext save:&error]) {
DLog(#"%#", [error localizedDescription]);
[persistentStoreCoordinator unlock];
[self performSelectorOnMainThread:#selector(handleSaveError:) withObject:nil waitUntilDone:NO];
}
} #catch (NSException *e) {
DLog(#"%#", [e description]);
[persistentStoreCoordinator unlock];
} #finally {
// pass
}
[dnc removeObserver:self name:NSManagedObjectContextDidSaveNotification object:threadManagedObjectContext];
[self performSelectorOnMainThread:#selector(parserFinished:) withObject:nil waitUntilDone:NO];
}
// Merging changes causes the fetched results controller to update its results
- (void)threadControllerContextDidSave:(NSNotification*)saveNotification {
// need to unlock before we let main thread merge
[persistentStoreCoordinator unlock];
[self performSelectorOnMainThread:#selector(mergeToMainContext:) withObject:saveNotification waitUntilDone:YES];
}
- (void)mergeToMainContext:(NSNotification*)saveNotification {
NSError *error;
[managedObjectContext mergeChangesFromContextDidSaveNotification:saveNotification];
if (![managedObjectContext save:&error]) {
DLog(#"%#", [error localizedDescription]);
[self handleSaveError:nil];
}
}
`
You have to follow the rule:
NSManagedObjectContext must be created on the same thread which uses
it. (OR in other words, every Thread must have its own MOC)
Violation of above rule cause the following exception:
Exception in *** -_referenceData64 only defined for abstract class. Define -[NSTemporaryObjectID_default _referenceData64]!,
Another problem somebody might face is, if using the NSFetchedResultsController, the delegates will not be called on the UI classes.
I hope this answer will help someone!
Short Answer:
you must use the following functions while saving context to ensure block operations are executed on the queue specified for the context.
perform(_:)
performAndWait(_:)
as Mentioned in the Apple Documentation of NSManagedObject Concurrency section
here
Long Answer:
As Mentioned in Apple Documentation
Core Data uses thread (or serialized queue) confinement to protect
managed objects and managed object contexts (see Core Data Programming
Guide). A consequence of this is that a context assumes the default
owner is the thread or queue that allocated it—this is determined by
the thread that calls its init method. You should not, therefore,
initialize a context on one thread then pass it to a different thread.
Instead, you should pass a reference to a persistent store coordinator
and have the receiving thread/queue create a new context derived from
that.
From this we can assume
Thread which create NSManagedObject will own that object
you can not create NSManagedObject in one thread and save it in an
other object(cause of error)
either create Separate NSManagedObject for each thread or pass it by
id as answered by Adriaan.
This will arise a new question in you mind
Is there a way to figure out what thread an NSManagedObjectContext is
on?
which is already answered here
and Tom Harrington answer will prove every thing above a little bit inaccurate(I also believe that) and redirect us back to the short answer:)
Do your mapping of nsmanagedobject data and saving of managedobjectcontext in the following block so it locks the managedobjectcontext from accessing by another thread and resolved the crash.
[context performBlockAndWait:^{
//your code
[context save:&error];
}];
sorry for my english (i'm french).
I did have the same issue and I realized that I called a method on Core Data Framework (inserting an object) from a second thread. I just call this method from the Main Thread using performSelectorOnMainThread and it resolve my problem.
I hope it will help you.
Thanks everyone, I was able to get rid of the pesky exception by following your tips.
It was the threading issue that seemed to cause the exception. In my case, I had the main thread spawning worker threads that fetch the XML, parse, create the necessary managed object, and save them.
I tried a lazy way out by using performSelectorOnMainThread for saving but that didn't work.
My final approach was to create a class called ThreadDataService with it's own ManagedObjectContext and each thread has one instance of ThreadDataService, basically what Adriaan had suggested.
Again, thanks for all the answers. You guys rock!
I had the same issue and when searching for an answer I found this. My problem was that I started 2 threads which worked on the same managed context is crashed when saving to the persistent store- if each thread has it own context the issue do not arise. But it might has been resolved by just locking the persistent store, but I believe the 2 managed contexts is the right solution.
Regards

Importing large datasets on iPhone using CoreData

I'm facing very annoying problem. My iPhone app is loading it's data from a network server. Data are sent as plist and when parsed, it neeeds to be stored to SQLite db using CoreData.
Issue is that in some cases those datasets are too big (5000+ records) and import takes way too long. More on that, when iPhone tries to suspend the screen, Watchdog kills the app because it's still processing the import and does not respond up to 5 seconds, so import is never finished.
I used all recommended techniques according to article "Efficiently Importing Data" http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/CoreData/Articles/cdImporting.html and other docs concerning this, but it's still awfully slow.
Solution I'm looking for is to let app suspend, but let import run in behind (better one) or to prevent attempts to suspend the app at all. Or any better idea is welcomed too.
Any tips on how to overcome these issues are highly appreciated!
Thanks
Instead of pushing plist files to the phone, you might want to send ready to use sqlite files. This has many advantages:
no need to import on the phone
more compact
If you always replace the whole content simply overwrite the persistent store in the device. Otherwise you may want to maintain an array as plist with all sqlites you have downloaded and then use this to add all stores to the persistentStoreCoordinator.
Bottom line: use several precompiled sqlite files and add them to the persistentStoreCoordinator.
You can use the iPhone Simulator to create those CoreData-SQLite-Stores or use a standalone Mac app. You will need to write both of those yourself.
First, if you can package the data with the app that would be ideal.
However, assuming you cannot do that then I would do then following:
Once the data is downloaded break it into multiple files before import.
Import on a background thread, one file at a time.
Once a file has been imported and saved, delete the import file.
On launch, look for those files waiting to be processed and pick up where you left off.
Ideally sending the data with the app would be far less work but the second solution will work and you can fine-tune the data break up during development.
I solved a similar problem by putting the insert processing in a background thread. But first I created a progress alert so the user couldn't manipulate the data store while it was inserting the entries.
This is basically the ViewControllers viewDidLoad
- (void)viewDidLoad
{
[super viewDidLoad];
NSError *error = nil;
if (![[self fetchedResultsController] performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
// Only insert those not imported, here I know it should be 2006 entries
if ([self tableView:nil numberOfRowsInSection:0] != 2006) {
// Put up an alert with a progress bar, need to implement
[self createProgressionAlertWithMessage:#"Initilizing database"];
// Spawn the insert thread making the app still "live" so it
// won't be killed by the OS
[NSThread detachNewThreadSelector:#selector(loadInitialDatabase:)
toTarget:self
withObject:[NSNumber numberWithInt:[self tableView:nil
numberOfRowsInSection:0]]];
}
}
The insert thread was done like this
- (void)loadInitialDatabase:(NSNumber*)number
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int done = [number intValue]+1; // How many done so far
// I load from a textfile (csv) but imagine you should be able to
// understand the process and make it work for your data
NSString *file = [NSString stringWithContentsOfFile:[[NSBundle mainBundle]
pathForResource:#"filename"
ofType:#"txt"]
encoding:NSUTF8StringEncoding
error:nil];
NSArray *lines = [file componentsSeparatedByString:#"\n"];
float num = [lines count];
float i = 0;
int perc = 0;
for (NSString *line in lines) {
i += 1.0;
if ((int)(i/(num*0.01)) != perc) {
// This part updates the alert with a progress bar
// setProgressValue: needs to be implemented
[self performSelectorOnMainThread:#selector(setProgressValue:)
withObject:[NSNumber numberWithFloat:i/num]
waitUntilDone:YES];
perc = (int)(i/(num*0.01));
}
if (done < i) // keep track of how much done previously
[self insertFromLine:line]; // Add to data storage...
}
progressView = nil;
[progressAlert dismissWithClickedButtonIndex:0 animated:YES];
[pool release];
}
It's a bit crude this way, it tries to init the data storage from where it left of if the user happend to stop it the previous times...
I had a similar problem importing many objects into CoreData. Initially i was doing a save on the managed object context after every object i wished to create & insert.
What you should do is create/initialize each object you want to save in CoreData, and after you have looped through all your remote data + created the objects, do a managed object context save.
I guess you could look at this as doing doing a transaction in a SQLite database: begin transaction, do lots of inserts/updates, end transaction.
if this still is too lengthy, just thread the darn task and prevent user interaction until complete
Is there any way you can pack the data ahead of time - say during development? And when you push the app to the store, some of the data is already there? That'll cut down on the amount of data you have to pull, thus helping to solve this issue?
If the data is time sensitive, or not ready, or for whatever reason you can't do that, could you compress the data using zlib compression before you ship it over the network?
Or is the problem that the phone dies doing 5K+ inserts?
I imagine you aren't showing all 5K records to the client? I'd recommend doing all of the aggregation you need on the server, and then only sending the necessary data to the phone. Even if this involves generating a few different data views, it'll still be orders of magnitude faster than sending (and then processing) all those rows in the iPhone.
Are you also processing the data in a separate (non event/ui) thread?
Any chance you can setup your server side to expose a RESTful web service for processing your data? I had a similar issue and was able to expose my information through a RESTful webservice. There are some libraries on the iphone that make reading from a webservice like that very easy. I chose to request JSON from the service and used the SBJSON library on the iphone to quickly take the results I got and convert them to dictionaries for easy use. I used the ASIHTTP library for making the web requests and queueing up follow up requests and making them run in the background.
The nice thing about REST is that it a built in way for you to grab batches of information so that you don't need to arbitrarily figure out how to break up your files you want to input. You just setup how many records you want to get back, and the next request you skip that many records. I don't know if that is even an option for you, so I'm not going into a lot of code examples right now, but if it is possible, it may be a smooth way to handle it.
Lets accept that Restful (lazy loading) is not an option... I understand you want to replicate. If the load problem is of the type 'less and less rows loading in more and more time) then in psuedo code...
[self sQLdropIndex(OffendingIndexName)]
[self breathInOverIP];
[self breathOutToSQLLite];
[self sQLAddIndex(OffendingIndexName)]
This should tell you lots.
I work on an app that regularly has to process 100K inserts, deletes, and updates with Core Data. If it is choking on 5K inserts, there is some optimization to be done.
Firstly, create some NSOperation subclass for processing the data. Override its -main method to do the processing. This method is, however, not guaranteed to run on the main thread. Indeed, its purpose is to avoid executing costly code on the main thread which would affect the user experience by making it freeze up grossly. So within the -main method, you need to create another managed object context which is the child of your main thread's managed object context.
- (void)main
{
NSManagedObjectContext *ctx = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[ctx setPersistentStoreCoordinator:mainManagedObjectContext.persistentStoreCoordinator];
[ctx setUndoManager:nil];
// Do your insertions here!
NSError *error = nil;
[ctx save:&error];
}
Given your circumstances, I don't believe you need an undo manager. Having one will incur a performance penalty because Core Data is tracking your changes.
Use THIS context to perform all of your CRUD actions in the -main method, then save that managed object context. Whatever owns your main thread's managed object context must register to respond to the NSNotification named NSManagedObjectContextDidSaveNotification. Register like so:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(mocDidSaveNotification:) name:NSManagedObjectContextDidSaveNotification object:nil];
Then define that selector:
- (void)mocDidSaveNotification:(NSNotification *)notification
{
NSManagedObjectContext *ctx = [notification object];
if (ctx == mainManagedObjectContext) return;
[mainManagedObjectContext mergeChangesFromContextDidSaveNotification:notification];
}
When all of this comes together, it will allow you to perform long-running operations on background threads without blocking the UI thread. There are several variations of this architecture, but the central theme is this: processing on BG thread, merge on main thread, update your UI. Some other things to keep in mind: (1) keep an autorelease pool around during your processing and drain it every so often to keep your memory consumption down. In our case, we do it every 1000 objects. Adjust for your needs, but keep in mind that draining can be expensive depending on the amount of memory required per object, so you don't want to do it too often. (2) try to pare your data down to the absolute minimum that you need to have a functional app. By reducing the amount of data to parse, you reduce the amount of time required to save it. (3) by using this multithreaded approach, you can concurrently process your data. So create 3-4 instances of your NSOperation subclass, each of which processes only a portion of the data so that they all run concurrently, resulting in a smaller amount of real time consumed for parsing the data set.