Part of my job involves working with existing projects, some with overly complicated Core Data implementations. Usually clients request that I help fix performance problems in the project or at least clean them up. Here is how I approach each project:
1: Clear out the cobwebs
First thing I do is look through the whole project for unused classes, methods, variables, etc. For Objective-C projects, this is the one thing I use AppCode for. For Swift projects, there are tools such as fus to help. For one project, I was able to delete 30 thousand lines of code this way. No refactoring yet, simply deleting unused stuff. Most projects that have been around for longer than 6 months have unused bits to delete I've found.
Next I look for eveywhere an
NSManagedObjectContext is used. Anywhere a context is used, the operation/function/class should be told what context to use. Commonly in project that need help, this is not the case and operations are assuming certain contexts. If everything is neatly factored to be told which context to use, I'm empowered to change which context they use at will.
Once I have control over which context everything is using, I move everything to use one main context on the main thread. Conventional wisdom is that this is wrong and it technically is but this is only a temporary step. Some of what causes instability, performance problems, or confusion in projects is an entirely too-complicated context arrangement in the name of performance. Usually there are entirely too many in an unnecessary arrangement.
Once everything is on one context, I can whip out one of my favorite programs, Instruments, and run the time profiler on the app. There's no need to guess or speculate or brute force your way to performance. Instruments can instead find out exactly where the worst places are and I can do something about those specifically. During this phase, I determine which tasks in the app are slow and save a few runs for each tasks. It's hard to say if anything is improved if there is no "before" picture to compare to.
Armed with the knowledge of exactly what tasks are slow and exactly why these takss are slow, I can go about fixing them. Maybe the solution really is to move Core Data operations to a background context, but maybe it's not. It could be something more like fetching 1000 objects once instead of fetching one object at a time 1000 times. Maybe that slow fetch request needs to have a batch-size set or maybe the fetch needs to pre-fetch related entities. I try to only make the change that actually helps performance problems and leave behind simpler code everywhere else.