Core Data Violations – AllThatIsLeftToUsIsHonor

Core Data is a framework the provides us with the ability to represent the model layer of iOS app along with persistence features.

The 2 essential CoreData framework classes to know are:

  • NSManagedObjectContext
    • Apple describes this as a scratchpad in-memory for app to perform core data operations. Users can later decide if they want to save ( commit) these changes to the disk
    • We are allowed to create multiple NSManagedObjectContext and specify child-parent relationship among them. So any changes made to parent are automatically propagated to the child. Any changes made to the child are propagated to the parent when child.save() is called
  • NSManagedObject
    • All the data stored in the Core Data database , when retrieved in memory is represented by Core Data framework in form of NSManagedObjects

In the most standard scenarios, we can get away with single NSManagedContextObject created with MainQueueConcurrencyType. In this case, all the Core Data operations happen on the main queue along with UI related actions. This setup is recommended only if the core data actions support the user actions ( Eg: A new TODO item has been created, an existing work item is marked completed etc).

But for certain apps, the core data operations do not have direct correlation with user’s scenario. Eg: Import operations, storing network data in background so that user’s can access it later, if necessary. As for any multi-threaded system  operations that do not directly correlate with user scenario should be performed on a background thread/queue. Core Data framework hence provides us with the option of creating a NSManagedContext with  PrivateQueueConcurrencyType. It goes a step further and also creates a private queue that is exclusively reserved for this NSManagedContext object.

Below are some of the rules that must be followed if you do not want your app that uses CoreData to crash (or) corrupt the database:

  • A NSManagedObjectContext should be used only on the queue that is associated with it.
    • If initialized with .PrivateQueueConcurrencyType, a private, internal queue is created that is associated with the object. This queue can be accessed by instance methods .performBlockAndWait (for sync ops) and .performBlock (for async ops)
    • If initialized with .MainQueueConcurrencyType, the object can be used only on the main queue. The same instance methods ( performBlock and performBlockAndQueue) can be used here as well.
  • An NSManagedObject should not be used outside the thread in which it is initialized

In order to stress the importance of these rules, Apple has associated a scary name when these rules are not followed – “Core Data Violations”.  In fact, Apple considered these violations so important, that they decided to start crashing when a CoreData violation is present, starting iOS 6. But there were already so many apps in App Store and most of them would start crashing and so Apple to turn it OFF.

Today it is still possible to get your app into AppStore with Core Data Violations in it. Refer to this blog post to understand how to enable this detection when developing your app. When this Core Data Violations detection is enabled, all violations of Core Data threading rules will cause debugger to stop with stack trace ending at

[NSManagedObjectContext __Multithreading_Violation_AllThatIsLeftToUsIsHonor__]

and hence the title of this blog post

Core Data Violations – AllThatIsLeftToUsIsHonor

Core Data Debugging Nugget

iOS developers seem to be split about Core Data framework. I have seen people who feel like Core Data “just makes sense” for them to those who consider Core Data is very hard to comprehend. I definitely belonged to those in the latter.

Due to a crash in Core Data code at work, I had to take a closer look at Core Data framework ( Original author the core data code left team and I am the one who drew the short straw for ownership). This code has been working , sans crashes, for a few releases now, but since iOS 9.3 it has started randomly crashing in production ( with of course no reliable repro steps). The exact code that has crashed has not been touched for a few releases.

Due to apparent lack of explanation, I dove deep into how to debug Core data code and found this debugging nugget.

Core data debugging nugget

  • Create a new Xcode scheme by selecting the tab in top left corner right before the simulator/device type and selecting “New Scheme”. Name it something eay to understand like “TestCoreData” (Using Ray Wenderlich video tutorial project as example here) Screen Shot 2016-04-17 at 10.32.02 AMScreen Shot 2016-04-17 at 10.32.49 AM
  • This new scheme will not be the default selected scheme. Click on the “Scheme” tab again and this time select “Edit scheme” as shown below

Screen Shot 2016-04-17 at 10.38.24 AM

  • Select the “Run” option from the left menu and select the “Arguments” tab from the right side wizard. Here click on the “+” sign under the “Arguments passed on launch” tab. Here enter the text                                                                            “-com.apple.CoreData.ConcurrencyDebug 1” and hit “Close” at the right bottom corner of the wizard.

Screen Shot 2016-04-17 at 10.33.15 AM

The next time you launch you app with this scheme selected and if you are using your CoreData layer incorrectly, you will get a crash that is similar to the below with the entry at end of your stack trace pointing to “NSManagedObjectContext_Multithreading_Violation_AllThatIsLeftToUsIsHonor__ ” providing you the indication that you are creating your NSManagedObjectContext object (or) NSManagedObject object in one thread and using it in an another ( which is CoreData violation). temporaryCoreData

An interesting side note is that, as per Marcus Zarra, this was enabled by default in iOS 6 but then was removed because this would too many production apps to crash.

I personally think that this is a very vital part of Core Data development as it would avoid random crashes due to  CoreData  threading violations that are virtually impossible to root cause.

I have redesigned our app’s Core data infrastructure to take advantage of specifying parent-child relationship among NSManagedObjectContext objects while also fixing the CoreData violations across our app.  I have also created a new scheme that will be shared across the team for Core Data debugging and which will also be enabled by default in Jenkins when buiding “dogfood” version of our app.

 

Resources where I discovered this nugget

http://oleb.net/blog/2014/06/core-data-concurrency-debugging/

http://nshipster.com/launch-arguments-and-environment-variables/

 

 

Core Data Debugging Nugget