Core Data does not bring relationships to a faulted object into memory until you access them. This behavior allows Core Data to conserve memory by keeping unused objects out of memory until you need them. In this example, Core Data is preventing only one object, the Location, from being loaded. You may be thinking that this doesn ’ t save too much memory. That is true, but imagine if the location object were also related to a series of other objects, each with its own relations, and so on. In large object hierarchies, the memory conserved by faulting can be considerable. Data Store Types When using Core Data, you can specify the type of data store that Core Data uses to persist your data. Your options are SQLite, binary, and in - memory. Although you will usually use the SQLite store type, you will take a brief look at all of the types. The in - memory store does not actually persist your data at all. As the name says, the data store is in memory. When your application quits, your data is gone. This could be useful in situations where you want to use Core Data to manage your application data when that data does not need to be persisted from session to session. This is typically not very useful in iOS applications as your application session could end at any time, particularly if the user of an iPhone gets a call. The binary store type uses a proprietary binary format to store your data. The primary drawback of using the binary format is that all of the data stored in the data store is loaded into memory when the data store is loaded. There is no provision to leave parts of the data on disk. In contrast, an SQLite database remains largely left on disk and Core Data only brings the parts that you specifi cally query for into memory. Although there is no “ disk ” on the iPhone or iPad, there is a difference between application memory and storage memory. When I say “ on disk, ” I am referring to the space on the device that is used for data storage. The iPhone or iPad cannot use this storage space to execute applications, so there is a distinction between the application memory available for use by your application ’ s execution and disk space, which is available for data storage. Storing Binary Data You may be tempted to store binary data such as images or sound clips in your Managed Objects using Core Data. In general, this is not a good idea. You are generally better off storing the binary data on disk and storing the path to the data in Core Data as you did in the catalog example earlier in the book because you may store a large chunk of data in an object and not realize that you are bringing all of that data into memory when Core Data loads the object. For example, if you stored the images for each of your catalog items in the database, but did not display them on the main catalog screen, these potentially large images would still be loaded into memory. If you do store binary data in Core Data, do not store the data in frequently accessed rows. For example, you should not store an image in the same managed object that you use to display items in a table if you are not also displaying the image because the image will be loaded into memory regardless of whether you display the image or not. Core Data Performance ❘ 259 CH009.indd 259CH009.indd 259 9/18/10 9:58:05 AM9/18/10 9:58:05 AM 260 ❘ CHAPTER 9 CORE DATA MIGRATION AND PERFORMANCE Entity Inheritance You may remember from Chapter 6 that you can implement an entity hierarchy in Core Data using inheritance and the parent fi eld of an entity in the entity detail pane. I have illustrated a hypothetical entity hierarchy in Figure 9 - 8. Although this is a feature of Core Data, you need to be aware of how the implementation uses memory in the back - end SQLite data store. An entity hierarchy is not the same as the equivalent object hierarchy in an object - oriented design. In the entity hierarchy, all of the attributes of all of the entities in the hierarchy are stored in the same database table. This can lead to ineffi ciency in how the data is stored on disk, causing excessive memory usage. To illustrate this, imagine that in the hierarchy illustrated in Figure 9 - 8, the Product entity has attributes P1 and P2, the Hammer has attributes H1 and H2, and the Screw has attributes S1 and S2. In the current implementation of Core Data, Core Data stores the data for all three of these entities in a single table, as illustrated in Figure 9 - 9. You can see that the Hammer entities have unused space in the table for the Screw - related fi elds and vice versa for Screw objects. Although this is a simple example, it illustrates the storage issue. The problem gets worse as your inheritance hierarchy gets larger and deeper. Runtime Performance This chapter covered improving perceived performance with threading and being conscious of how Core Data is using memory. In this section, I will just lay out a few general tips that you can look to in an attempt to increase the runtime performance of your application. First, when designing your model, avoid over - normalization of your data. Remember that you are not designing a relational database but an object persistence mechanism. Feel free to de - normalize your data if it makes building the displays for the application easier. You should try to fi nd a balance between normalization and speeding up your queries for rapid display. Accessing data through a relationship is more expensive than retrieving an attribute. Consider this before normalizing your data. In addition, querying across relationships is expensive. Determine if you really need to do it, or de - normalize the data if it makes sense. For example, if you have an application that stores names and addresses, it may make sense from a UI perspective to keep the state name in the same table as the rest of the address as opposed to normalizing it out into its own table. You may not want to FIGURE 9 - 8: Entity inheritance hierarchy FIGURE 9 - 9: Core Data storage for entity inheritance CH009.indd 260CH009.indd 260 9/18/10 9:58:06 AM9/18/10 9:58:06 AM take the overhead penalty for following a relationship to a state table every time that you need to look up an address if you will always be showing the address and state together. I know that it may seem obvious, but you should try to fetch only the data that you need when you need it. This tip ties in with the “ Faulting ” section from earlier in the chapter. Core Data will generally not bring in data that you are not going to use. Therefore, you should be careful to avoid building your queries in a way that forces Core Data to bring data into memory that you may not need. Another thing that you can do to increase your application performance is take advantage of Core Data caching. The Persistent Store Coordinator holds fetched results in its caches. This is particularly useful when you can set up a background thread to fetch data while the foreground remains responsive. Then, the foreground thread can read the data from the persistent coordinator ’ s cache when necessary, avoiding another trip to the data store. The fi nal tip has to do with the order of the items in your search predicate. Search order in predicates is important. Put likely - to - fail criteria fi rst so the comparison can end quickly. The engine evaluates predicates in order, and if one part of the predicate fails, the engine will move on to the next record. You can potentially reduce query times by placing likely - to - fail criteria at the beginning of a predicate if it makes sense. Managing Changes with the Fetched Results Controller In this section, you will look at how you can use NSFetchedResultsController to update your TableView based on changes to a result set. When the data managed by NSFetchedResultsController changes, the fetched results controller calls several delegate methods. You have the option to either use these delegate methods to update the associated TableView based on each individual change, or simply handle one delegate method and tell the TableView to reload its data. Implementing the delegate methods to handle individual updates can yield better performance because you are not reloading all of the table data each time there is an update. There is a tradeoff, however, because if there are many simultaneous changes to the data, it may be cheaper to just reload all of the data because presenting many individual changes can be time consuming. To keep the Tasks application simple, I took the latter approach. In the RootViewController , you implemented the controllerDidChangeContent delegate method to reload the data for the TableView. Here is the implementation: - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { [self.taskTableView reloadData]; } RootViewController.m Compare how you just reloaded all of the data for the table in the Tasks application to how you handle individual cell updates in the random numbers application. In the random numbers project, you handled each update individually by accepting the template code for the RootViewControllers . The delegate methods that the NSFetchedResultsController calls Core Data Performance ❘ 261 CH009.indd 261CH009.indd 261 9/18/10 9:58:06 AM9/18/10 9:58:06 AM 262 ❘ CHAPTER 9 CORE DATA MIGRATION AND PERFORMANCE are controllerWillChangeContent :, controller:didChangeSection:atIndex:forChangeType , controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: , and controllerDidChangeContent :. The FetchedResultsController calls the controllerWillChangeContent: and controllerDidChangeContent : methods to bracket the changes that are being made to the result set. You implement these methods to tell the table that changes will begin and end respectively. You can think of these methods as beginning and committing a transaction to the table. Here is the implementation in the RandomNumbers example: - (void)controllerWillChangeContent:(NSFetchedResultsController *)controller { [self.tableView beginUpdates]; } - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { [self.tableView endUpdates]; } RootViewController.m This code simply tells the TableView that updates are coming and that updates are fi nished. The endUpdates call triggers the TableView to display and optionally animate the changes to the data. The FetchedResultsController calls the controller:didChangeSection:atIndex: forChangeType method when the sections of the TableView should change. The two types of changes that you will receive in this method are NSFetchedResultsChangeInsert and NSFetchedResultsChangeDelete . Your code will be able to determine if the changes to the data have added or removed a section from the TableView. Typically, you will implement a switch statement to handle these two cases, as shown in the RandomNumbers sample: - (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id < NSFetchedResultsSectionInfo > )sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type { switch(type) { case NSFetchedResultsChangeInsert: [self.tableView insertSections: [NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeDelete: [self.tableView deleteSections: [NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; break; } } RootViewController.m CH009.indd 262CH009.indd 262 9/18/10 9:58:07 AM9/18/10 9:58:07 AM This code simply inserts or deletes the section that it receives in the method call. Finally, the FetchedResultsController calls the controller:didChangeObject:atIndexPath: forChangeType:newIndexPath: method when objects in the data store change. In this method, your code needs to handle these four operations: NSFetchedResultsChangeInsert , NSFetchedResultsChangeDelete , NSFetchedResultsChangeMove , and NSFetchedResultsChangeUpdate . Again, you will usually handle calls to this method in a switch statement and make the appropriate changes to your TableView as you did in the RandomNumbers example: - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath { UITableView *tableView = self.tableView; switch(type) { case NSFetchedResultsChangeInsert: [tableView insertRowsAtIndexPaths: [NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeDelete: [tableView deleteRowsAtIndexPaths: [NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeUpdate: [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath]; break; case NSFetchedResultsChangeMove: [tableView deleteRowsAtIndexPaths: [NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; [tableView insertRowsAtIndexPaths: [NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; break; } } RootViewController.m Core Data Performance ❘ 263 CH009.indd 263CH009.indd 263 9/18/10 9:58:07 AM9/18/10 9:58:07 AM 264 ❘ CHAPTER 9 CORE DATA MIGRATION AND PERFORMANCE PERFORMANCE ANALYSIS USING INSTRUMENTS When confronted with a performance issue in your application, the fi rst place that you should turn is Instruments. Instruments is a GUI - based tool that you can use to profi le your application in myriad ways. Instruments features a plug - in architecture that allows you to pick and choose from a library of recording instruments to use in profi ling your application, as shown in Figure 9 - 10. Additionally, you can create custom instruments that use DTrace to examine the execution of your application. Once you have selected the recording instruments that you would like to use, you can use Instruments to run your application, either in the iPhone Simulator or on the actual device, and gather the data that you need to optimize your application. After you start your application, Instruments will profi le the application in the background as the application runs. As your application is running, you will see the graphs in Instruments react to operations that you perform in the application. The main feature of the tool ’ s interface is the timeline. The premise is that you are recording the operation of your application in real - time and can later go back and look at the state of your application at any time during your test run. This is an incredibly powerful tool to use when trying to fi nd problems in your application. Once you narrow down a particular problem to a specifi c time on the timeline, you can drill down into the application stack. The stack provides information on the code that was executing at the time that Instruments took the sample. You can select your code segments in the stack to view the source code. Typically, the biggest problems that you will fi nd in your application are issues with memory management and leaks. Instruments has a couple of tools, ObjectAlloc and Leaks, that can be extremely helpful in detecting and crushing these often - illusive bugs. You will take a closer look at using these tools in Appendix A. Instruments is an incredibly powerful tool and I cannot recommend it enough. If you have never used Instruments, you owe it to yourself to take a closer look at the tool. In this section, I walk you through the use of Instruments to profi le your Core Data application using the Core Data – related tools. Starting Instruments In general, you can either start the Instruments tool by simply launching the application that is located, by default, in /YourDriveName/Developer/Applications or by selecting Run ➪ Run With Performance Tool from the menu bar in Xcode. Unfortunately, Apple does not support the FIGURE 9 - 10: Instruments tool library CH009.indd 264CH009.indd 264 9/18/10 9:58:08 AM9/18/10 9:58:08 AM Core Data instruments for use with the iPhone or iPad. However, there is a workaround that you can use because Core Data notifi cations are broadcast system - wide and the iPhone simulator is not sandboxed from the rest of the Mac OS X operating system. Even though Core Data is grayed out as an option in the “ Run with Performance Tool ” menu item, you can use the Core Data instruments with the Instruments tool. You fi rst need to get Instruments to recognize your application as an instrument target. Open the RandomNumbers project in Xcode. The easiest way to get your project into instruments is to use the Run with Performance Tool ➪ CPU sampler menu item in Xcode. This will fi nd your project binary and start it in Instruments while running the CPU sampler. Because you want to use the Core Data tools and not the CPU sampler, you need to stop the Instruments session and add the Core Data tools to the instrumentation. Press the red button to stop recording. Now, Instruments knows where to fi nd the RandomNumbers application. Close the current window and discard the results of the CPU sampler run. From the Instruments menu bar, select File ➪ New to create a new instrument session. The new dialog displays a set of templates that you can use to create your Trace Document. Select All under Mac OS X in the left pane and select Core Data in the right pane. Finally, click Choose to create your new document. You should see a new Instruments window with three Core Data Instruments in the left - hand Instruments pane. You now have to tell Instruments which program you want to execute. Open the Default Target pull down at the top of the window. Select Launch Executable ➪ RandomNumbers as shown in Figure 9 - 11. FIGURE 9 - 11: Setting the Default Target Performance Analysis Using Instruments ❘ 265 CH009.indd 265CH009.indd 265 9/18/10 9:58:09 AM9/18/10 9:58:09 AM 266 ❘ CHAPTER 9 CORE DATA MIGRATION AND PERFORMANCE You are now ready to begin your test run. Click the red Record button in the top left of the application to start your recording. You will see the RandomNumbers application start up in the simulator. Once the application starts, you should see the timeline begin to move indicating that Instruments is profi ling your application. You should also notice a spike in the top instrument, Core Data Fetches. This indicates that a fetch has occurred in the application. Tap the plus button in the simulator to generate some random numbers. In 5 seconds, you should see a spike in the Core Data Saves instrument indicating that a save has occurred. Stop the recording by clicking the red button. The Instruments Interface Now that you have fi nished your test run, you will want to analyze your data. To do this, you should understand how to use the Instruments interface. You can see the interface as it should look having completed your test run in Figure 9 - 12. Instruments Pane Track Pane Detail Pane Extended Detail Pane FIGURE 9 - 12: The Instruments interface CH009.indd 266CH009.indd 266 9/18/10 9:58:09 AM9/18/10 9:58:09 AM The Instruments pane shows all of the instruments that are active for the current test run. The Track pane shows a graph representing different things for different instruments. For the Core Data Fetches instrument, the Track pane shows the count of items fetched and the duration of the fetch. The Track pane displays the time that an event occurred during the test run. You can adjust the time scale using the slider below the Instruments pane. You can also scroll the Track pane using the scrollbar at the bottom of the pane. Below the Track pane is the Detail pane. Like the Track pane, the Detail pane shows different details based on the tool that you have selected in the Instruments pane. For the Core Data Fetches instrument, the Detail pane displays a sequence number, the caller of the fetch method, the fetched entity, the count of the number of items returned in the fetch, and the duration of the fetch in microseconds. You can select an item in the Detail pane to view more detail about the item in the Extended Detail pane. The Extended Detail pane is particularly useful because it shows a stack trace for the method call that you have selected in the Detail pane. Select a fetch in the Detail pane to view its stack trace. I fi nd it useful to display the fi le icons in the Extended Detail pane because doing this makes the calls in the stack that originated in your code obvious. You can enable the fi le icons by clicking on the gear icon in the Extended Details pane. In Figure 9 - 12, you can see that the fetch selected in the Detail pane is initiated in the code in the RootViewController viewDidLoad method. If you double - click on an item in the call stack that corresponds to one of your source code fi les, in this case the call to the RootViewController viewDidLoad method, Instruments will display the source code in the Detail pane, showing you the exact line of code that made the fetch request. Click the Event List View button at the bottom left of the Detail pane to get back from the source code to the list of events. The Core Data Instruments Four instruments are available for use with Core Data: Core Data Saves, Fetches, Faults, and Cache Misses. Core Data Saves reports the methods that invoke the Core Data save operation. It also reports the duration of the operation in microseconds. Opening the extended detail view shows the stack trace for the call and the time at which the call occurred. You can use this tool to discover how often you are saving data and fi nd a balance between saving too much and not saving enough. Each save operation causes a disk write, which can hurt performance, but not saving often enough results in using excess memory to hold the data in memory. Core Data Fetches reports all fetches made against Core Data. The tool reports the name of the fetched entity, the caller, the number of records returned, and the duration of the fetch in microseconds. Opening the extended detail view shows the stack trace for the call and the time at which the call occurred. Fetch operations are costly as they read from disk. You can use this tool to help optimize your search predicates to limit the number of rows returned by a fetch request. Performance Analysis Using Instruments ❘ 267 CH009.indd 267CH009.indd 267 9/18/10 9:58:10 AM9/18/10 9:58:10 AM 268 ❘ CHAPTER 9 CORE DATA MIGRATION AND PERFORMANCE Core Data Faults reports fault events that occur as the result of needing to realize object references in to - many relationships. The tool reports the method causing the fault, the object that was faulted, the execution duration in microseconds, the relationship fault source, the name of the relationship, and the relationship fault duration in microseconds. You can open the extended detail view to see the stack trace for the call and the time at which the call occurred. As discussed earlier in this chapter, faulting is a memory - saving technique that comes at the expense of time when Core Data realizes the fault. If you fi nd that Core Data is spending an excessive amount of time resolving faults, but your memory consumption is low, you could consider pre - fetching related objects instead of having them realized through a fault. The Cache Misses tool reports fault events that result in cache misses. This is a subset of the information provided by the Faults instrument. The tool displays the method that caused the cache miss, the duration of execution for the fault handler in microseconds, the relationship cache miss source, the name of the relationship, and the relationship cache miss duration. Opening the extended detail view shows the stack trace for the call and the time at which the call occurred. Similar to the remedy for extensive faulting, you can mitigate cache misses by pre - fetching data. If data is not in the cache, it leads to an expensive read from disk operation. MOVING FORWARD In this chapter, you learned how to version your data models and migrate between versions to help you add new features to your database and application. I also covered how to safely use Core Data in a threaded application with NSOperation to increase performance. You looked at some Core Data performance considerations and tips. Finally, you learned how to use the Instruments tool to observe your application ’ s performance when using Core Data. This ends the section of the book on Core Data. You should now feel confi dent that you are able to implement a data - driven application using this exciting and useful technology. You should also have all of the tools in your arsenal to be able to debug and troubleshoot problems that may arise while you are using Core Data. In the next section of the book, you learn how to use XML and Web Services in your applications to communicate with other applications over the Internet. CH009.indd 268CH009.indd 268 9/18/10 9:58:10 AM9/18/10 9:58:10 AM . the iPhone or iPad, there is a difference between application memory and storage memory. When I say “ on disk, ” I am referring to the space on the device that is used for data storage. The iPhone. predicates is important. Put likely - to - fail criteria fi rst so the comparison can end quickly. The engine evaluates predicates in order, and if one part of the predicate fails, the engine. Instruments is a GUI - based tool that you can use to profi le your application in myriad ways. Instruments features a plug - in architecture that allows you to pick and choose from a library