Debugging Core Data Objects • catchsomeair.us
The Note and Tag entities share a many-to-many relationship. Build and run One of the techniques Core Data uses to accomplish this is faulting. . For example, what happens if a fault is fired for data that no longer exists?. I did not really need to use Core Data, but I wanted to gain some experience Define entities with properties (called attributes) and relationships with other entities (one-to-one, . fault, faultingState, hasChanges, and entity. Faulting is one of the techniques Core Data uses to consume as little memory as possible. Core Data understands that there's no need to load the relationship. Core Data can only do this by firing the relationship fault.
Clone or download the repository from GitHub and open the project in Xcode 8 or higher. Before we run our experiment, open the data model of the project by selecting Notes. Switch to the graph style of the data model editor to get an idea of the entities, attributes, and relationships defined in the data model.
Notes is an application that manages the user's notes. A note has a title and contents. It also knows when it was created and last updated.
A note can be associated with a category and a category can contain multiple notes. In other words, the Note and Category entities share a one-to-many relationship. A note can also have tags and a tag can be linked to multiple notes.
The Note and Tag entities share a many-to-many relationship. Build and run the application to give it a try. Make sure Notes contains a few notes, a few categories, and a few tags. That is important for our experiment. Execute the fetch request and print the first note to the console. Run the application to see the result. We fetched a record of the Note entity. But notice that the data associated with the record is missing.
Instead we see the word fault. As you know, Core Data can only operate on records of the persistent store once they are loaded into memory. This is only possible because Core Data is heavily optimized to keep its memory footprint as low as possible. One of the techniques Core Data uses to accomplish this is faulting. But the team at Apple didn't invent faulting.
Several other frameworks use a similar strategy to accomplish similar goals. Ruby on Rails and Ember come to mind.
Even though faulting may look mysterious at first, the idea is simple. Core Data only fetches the data it absolutely needs to satisfy the needs of your application. That is faulting in a nutshell. The idea of faulting is simple, but the underlying implementation is an advanced bit of programming.
Fortunately, we don't have to worry about that. That is the responsibility of the framework. Let me show you how it works with another example. Below the print statement, we safely unwrap the value of the title property and print it to the console and we add another print statement for the note. We print the note to the console, ask the value of one of the properties of the note, and print the note again.
Why we do that becomes clear when we inspect the results in the console. Run the application and take a look at the output in the console. Despite this fault, we can access the value of the title property and print it to the console.
This is confirmed by the third print statement in which we print the note again. What is happening here? We first asked for the user's notes and Core Data diligently gave us the list of notes. But, as you can see in the console, it is a list of empty records. From the moment we ask for the value of a property of one of the records, Core Data jumps into action and fetches the data from the persistent store.
It's not your fault
In some situations a managed object may be a fault—an object whose property values have not yet been loaded from the external data store. Faulting reduces the amount of memory your application consumes.
A fault is a placeholder object that represents a managed object that has not yet been fully realized or a collection object that represents a relationship: A managed object fault is an instance of the appropriate class, but its persistent variables are not yet initialized. A relationship fault is a subclass of the collection class that represents the relationship.
Faulting allows Core Data to put boundaries on the object graph. Because a fault is not realized, a managed object fault consumes less memory, and managed objects related to a fault are not required to be represented in memory at all. To illustrate, consider an application that allows a user to fetch and edit details about a single employee.
The employee has a relationship to a manager and to a department, and these objects in turn have other relationships.
It's not your fault
If you retrieve just a single Employee object from a persistent store, its manager, department, and reports relationships are initially represented by faults. Figure A department represented by a fault Although the fault is an instance of the Department class, it has not yet been realized—none of its persistent instance variables have yet been set. If it were a requirement that the object graph be complete, then to edit a single attribute of a single employee, it would ultimately be necessary to create objects to represent the whole corporate structure.
Firing Faults Fault handling is transparent—you do not have to execute a fetch to realize a fault. If at some stage a persistent property of a fault object is accessed, Core Data automatically retrieves the data for the object and initializes the object. This process is commonly referred to as firing the fault.
If you access a property on the Department object — its name, for example — the fault fires and Core Data executes a fetch for you to retrieve all of the object's attributes. See NSManagedObject for a list of methods that do not cause faults to fire.
Core Data automatically fires faults when a persistent property such as firstName of a fault is accessed. However, firing faults individually can be inefficient, and there are better strategies for getting data from the persistent store see Decreasing Fault Overhead. When a fault is fired, Core Data does not go back to the store if the data is available in its cache. With a cache hit, converting a fault into a realized managed object is very fast—it is basically the same as normal instantiation of a managed object.
If the data is not available in the cache, Core Data automatically executes a fetch for the fault object; this results in a round trip to the persistent store to fetch the data, and again the data is cached in memory.
Core Data Programming Guide: Faulting and Uniquing
Whether or not an object is a fault simply means whether or not a given managed object has all its persistent attributes populated and is ready to use. If you need to determine whether an object is a fault, call its isFault method without firing the fault without accessing any relationships or attributes.
If isFault returns NOfalse, then the data must be in memory and therefore the object is not a fault. However, if isFault returns YEStrue, it does not imply that the data is not in memory. The data may be in memory, or it may not, depending on many factors influencing caching. You are strongly discouraged from overriding description in this way.
There is no way to load individual attributes of a managed object on an as-needed basis and avoid realizing retrieving all property values of the entire object.