0

A lot of times I have a situation in my app where I'm pushing a view controller and fetching object from core data to fill the UI. Now I usually do this fetching in viewDidLoad on the main thread and I'm sure there is a better way to do this, maybe on different thread.

If indeed thread is the way to go here, I dont have a lot of experience with threads in objective c, so I will really appreciate if some one can show me code examples.

And also I know core data is not thread safe so should I do any extra work to be on the safe side?

1 Answer 1

2

You should probably be using NSFetchedResultsController. It will manage batching for you and only load what you need at the time. It's not just for table views. You can do this without a separate thread (as long as your DB isn't too big, and your fetch does not have too complex predicate that requires visiting every record in the DB).

As an alternative to FRC, or in the case of needing all records, you can use a separate thread, which will fetch objects, then present them to the main thread.

These are difficult concepts to explain in a single SO answer, so if it still does not make sense, consult the Core Data Concurrency Programming Guide.

Or, if you want to load it all, and have some control, you can do something like this. NOTE: I think this dual MOC pattern is the way to go anyway, because it makes it easier to do backgrounding anyway. For that matter, if you use UIManagedDocument, you get most of this for free anyway.

Note, this will probably take longer overall to load, but it will free up the UI thread.

NSManagedObjectContext *workerMoc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
workerMoc.persistentStoreCoordinator = psc;
// The "mainMoc" will be a child of the worker
NSManagedObjectContext *mainMoc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
mainMoc.parentContext = workerMoc;

Then when loading...

// The actual work will run in the background on the worker queue
// The "worker" queue will do the fetching and faulting, then pass object IDs
// to the mainMoc, which will fault its MOs from the parent
[workerMoc performBlock:^{
    NSFetchRequest *fetchRequest = // Create fetch request
    NSArray *fetchResults = [workerMoc executeFetchRequest:fetchRequest error:0];
    [fetchResults enumerateObjectsUsingBlock:^(NSManagedObject *obj, NSUInteger idx, BOOL *stop) {
        // Probably want to fault the object in before going to mainMoc
        // since that can also take some time...
        // Can also "batch" them in some small number of objects.
        NSManagedObjectID *objectID = obj.objectID;
        [mainMoc performBlock:^{
            NSManagedObject *fetchedObject = [mainMoc objectWithID:objectID];
            // Do something with it now that you are back on the main thread...
        }];
    }          
}];
Sign up to request clarification or add additional context in comments.

3 Comments

throughout all my app I use one moc and share it between all classes, Is this necessary to create two different moc's?
You must not access a MOC from multiple threads. So, you you want to do anything related to the database in a separate thread, you must use a different MOC. Note, however, the first sentence. In that case, if you just use a NSFetchedResultsController, you do not need a separate MOC because it will fetch on the main thread.
so the mainMOC can be the shared one and I just need to create the workMoc every time right? and I didn't understand what u saying about the NSFetchedResultsController, if I do use it, I still need to use it in separate thread no? It can also block the main thread no?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.