Mastering In CoreData (Part 10 NSFetchRequest)

Core Data

In previous part we looked some features of NSFetchRequest.This part is the continuation of the Part 9 of the series Mastering in CoreData. It is mandatory to look part 9 then come to this. So in part 9 we are in the heading on How to optimize fetch objects using NSFetchRequest hidden features .We will use previous part project so Download the starter project here and if you you have already delete the application first .

Note : Mandatory to look part 9

Optimization

Return objects as (Lazy load or Fault)

NSFetchRequest has a property “returnsObjectsAsFaults” which return faulty objects. Before we dive into this first we need to clear what is fault (Lazy loading) in Core Data

Faulting is one of the techniques that core data uses to keep its memory low without sacrificing performance and also decreases the fetch objects response time. Faulting isn’t unique to Core Data. A similar technique is used in many other frameworks, such as Ember and Ruby on Rails. The idea is simple, only load data when it’s needed.

As shown in Figure 1

Let’s suppose we have Entity A contains 100 attributes / properties . In persistent store we have 1000 records of Entity A was saved . When we fetched all data normally it will load all 1000 data in a cache(NSManagedObjectContext) each having 100 properties which takes time and also consumes memory.

If we do lazy loading or ask Managed Object Context to fetch data in faults What it will do it will return 1000 records metadata information (contains information for tracking) only which will be very fast and will not take much memory.
Note: When loading data using fault no property will instantiated or loaded into memory only meta data will load that can track object in a persistent store
Now when client tries to access property on first record of faulty Entity A object.It will load complete instance of that record with all the properties of particular record that was accessed which means 999 records still in faulty state only the record that was accessed will be loaded and we term that is used fault is fired.

Figure 1

To illustrate this example, let’s dive into the code. Delete the application first. As you can see in the Figure 2 we created three users with different firstName and secondName

Figure 2

To proved Figure 1 flow using code we performed number of tasks as shown in Figure 3

  1. Created NSFetchRequest using its instance property returnsObjectsAsFaults true. What we are saying to NSManagedObjectContext to return array of faulty objects. It’s default value is true. I added this line just to show faulty objects is returning due to this
  2. Printed faulty objects returned from the context. Fault object contains necessary metadata required to track the object. One information it get the NSManagedObjectId which is unique for every record stored
  3. As we illustrated in Figure 1 we fired fault by accessing any property. when We access property as shown in Figure 3.It asks the persistent store to load complete data into the Managed Object Context (Cache). Now if we again fetch objects with this flag value true since data was loaded completely it will simply return the complete object.

If you still don’t understand it. In later part we will go in depth working with fault objects. One thing you should take in this section is that returnsObjectsAsFaults flag in fetch request tells the persistent store to do lazy loading which is very memory efficient.

The default value is true. This setting is not used if the result type (see resultType) is NSManagedObjectIDResultType, as object IDs do not have property values.

You can set returnsObjectsAsFaults to false to gain a performance benefit if you know you will need to access the property values from the returned objects immediately. In short if you want to fetch objects and immediately populate fields there is no purpose of lazy loading at that time. Since firing a fault relative to normal could be expensive.

Figure 3

Which properties to fetch

A collection of either property descriptions or string property names that specify which properties should be returned by the fetch.

To illustrate this, we will use the project we created so far. Go to the “CrudOperationCoreData.xcdatamodeld” you will see User entity with three properties and two relationship properties. If we specify somehow in fetchrequest to load only firstName in memory than it should gain memory and performance improvement.

As you can see in the Figure 4 we asked fetch request to return firstname of all Users in the database and we also told it to return in Dictionary form. These are the following steps that we performed to achieve this

  1. Created fetchRequest with the array type NSDictionary.
  2. Using propertiesToFetch array type property in NSFetchRequest we tell Managed Object Context to bring only these properties. Secondly we make the result type to dictionaryResultType. Now fetch request will return array of dictionary in the results.In part 9 we looked result type in depth with code example . If you didn’t see part 9 it is mandatory to look in The result type of the fetch request section. As shown in Figure 4 we added firstName in propertiesToFetch list.
  3. We expected array of NSDictionary object in the results of fetch request and we printed in the console as you can see fetchRequest actually returned dictionary contains properties we told. Imagine it will surely improve the memory and response time if Entity have 100 of attributes.
Figure 4

In Figure 5 we used managedObjectResultType as a result type and the concept of partial fault for that we performed the number of tasks

  1. Created a fetch request to get array of User object instead of Dictionary
  2. This step is very important we tell fetch Request resultType to be managedObjectResultType. If we make managedObjectResultType result type and used propertiesTofetch feature it will do partial faulting which means it will load only those properties in memory which is defined in the propertiesTofetch list and the properties which is not defined will not be instantiated or loaded into memory . Note : This partial fault condition will applicable if returnsObjectsAsFaults = true. So many things if you are following part 9 and 10 you will get this. As shown in Figure 4 we added firstName property in propertiesTofetch and in the console when we access firstName fault was not fired since it was already loaded. When we access secondName property since it was not declare in properties to fetch list it will fired a fault and whole object will bring into the memory from the persistent store.
Figure 5

Note: properties to fetch works with only managedObjectResultType and dictionaryResultType, as in these we only expect property.

propertiesToFetch The property descriptions may represent attributes, To-One relationships, or expressions. Application will crash if you add relationship having ToMany type in the propertiesToFetch list. As shown in the Figure 6 application was crashed since we added “task” property in the list which has ToMany relationship with the User object.

Figure 6

Limit the Fetch Results

The fetch limit specifies the maximum number of objects that a request should return when executed.

As shown in Figure 7 three User objects present in the persistent store since we added limit of 1 it will return only one User object as shown in the console .

It has a performance benefits if your backend database is SQL

Figure 7

Batching Core Data

Let’s suppose we are executing a fetch request that returns about 2000 entities. which, is taking about 20 seconds on a device. So I thought I might set a fetch limit of 100, and then when the user scrolls to the end of the table view, fetch the next 100 entities. This can be accomplished using NSFetchRequest's setFetchLimit and setFetchOffset. Let’s add 10,000 of User objects in the persistent store as shown in Figure 8

Figure 8

In the Figure 9 we created batch fetching logic it will helpful in tableview with huge list population. In the meantime user scroll down data is loaded is batches which will appear while user is scrolling. One more than don’t do it in main thread we will fix this while we will be doing threading part. If you want other type batch see fetchBatchSize property in NSFetchRequest.

Figure 9

Summary

In this part 10 we looked NSFetchRequest in depth with coding and diagrams and also looked how can we improve memory and time using its hidden features.

Useful Links

https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CoreData/FaultingandUniquing.html

https://code.tutsplus.com/tutorials/what-is-a-core-data-fault--cms-25157

https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CoreData/FaultingandUniquing.html

https://developer.apple.com/documentation/coredata/nsfetchrequest/1506851-propertiestofetch

https://stackoverflow.com/questions/11165348/batching-core-data-fetch-results

Senior iOS Engineer | HungerStation | Delivery Hero