1. Trang chủ
  2. » Công Nghệ Thông Tin

Phát triển ứng dụng cho iPhone và iPad - part 30 pptx

10 165 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 10
Dung lượng 2,37 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Core Data does not bring relationships to a faulted object into memory until you access them.. This could be useful in situations where you want to use Core Data to manage your applicati

Trang 1

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

Trang 2

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

Trang 3

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

Trang 4

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

Trang 5

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

Trang 6

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

Trang 7

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

Trang 8

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

Detail Pane

Extended Detail Pane

FIGURE 9 - 12: The Instruments interface

Trang 9

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

Trang 10

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

Ngày đăng: 04/07/2014, 21:20

w