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

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

10 210 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,81 MB

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

Nội dung

Typical reasons for an error here include: * The persistent store is not accessible * The schema for the persistent store is incompatible with current managed object model Check the e

Trang 1

initWithManagedObjectModel:[self managedObjectModel]];

if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl

options:nil error: & error]) { /*

Replace this implementation with code to handle the error appropriately.

abort() causes the application to generate a crash log and terminate.

You should not use this function in a shipping application, although

it may be useful during development If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.

Typical reasons for an error here include:

* The persistent store is not accessible

* The schema for the persistent store is incompatible with current managed object model

Check the error message to determine what the actual problem was.

*/

NSLog(@”Unresolved error %@, %@”, error, [error userInfo]);

abort();

} return persistentStoreCoordinator;

}

TasksAppDelegate.m

The code fi rst determines if the Persistent Store Coordinator already exists If it exists, the method returns it to you

If the coordinator does not exist, the code must create it In order to create the coordinator, you need

to pass in a URL that points to the data store The template uses the fileURLWithPath: method on the NSURL class to create the URL You can see that the store name will be Tasks.sqlite

Next, the code allocates an NSError object to hold the error data that the Persistent Store Coordinator generates if there is a problem confi guring the coordinator

The next line allocates and initializes the coordinator using the Managed Object Model You will look at the managedObjectModel getter method in a moment Remember that you use the Persistent Store Coordinator to mediate between the Managed Object Context, the Managed Object Model, and the data store Therefore, it makes sense that the coordinator would need to have a reference to the model

Now that the coordinator knows about the model, the code goes on to tell it about the data store

The next line of code adds the data store to the coordinator You will notice that the type

of the data store is NSSQLiteStoreType This indicates that the template uses the SQLite backing data store If you wanted to use the binary store, you would change this enumeration value to

NSBinaryStoreType , and if you wanted to use the in - memory store, you would set the value

Trang 2

The rest of the code logs an error if there was a problem adding the SQLite store to the coordinator

If there was no error, the method returns the coordinator

The getter function to return the Managed Object Model is very straightforward:

- (NSManagedObjectModel *)managedObjectModel {

if (managedObjectModel != nil) {

return managedObjectModel;

}

managedObjectModel = [[NSManagedObjectModel

mergedModelFromBundles:nil] retain];

return managedObjectModel;

}

TasksAppDelegate.m

If the model already exists, the getter method returns it If not, the code creates a new Managed Object

Model by merging all of the model fi les contained in the application bundle The Tasks.xcdatamodel

fi le in the Resources folder contains the object model This fi le is included with the application bundle,

so this method takes that fi le and uses it to create the NSManagedObjectModel object

The last bit of interesting code in the App Delegate is the managedObjectContext getter method:

- (NSManagedObjectContext *) managedObjectContext {

if (managedObjectContext != nil) {

return managedObjectContext;

}

NSPersistentStoreCoordinator *coordinator =

[self persistentStoreCoordinator];

if (coordinator != nil) {

managedObjectContext = [[NSManagedObjectContext alloc] init];

[managedObjectContext setPersistentStoreCoordinator: coordinator];

}

return managedObjectContext;

}

TasksAppDelegate.m

This method works similarly to the previous two The fi rst thing it does is check to see if the context

already exists If the context exists, the method returns it

Next, the code gets a reference to the persistentStoreCoordinator If you refer back to

Figure 5 - 1, you will see that the Managed Object Context needs only a reference to the Persistent

Store Coordinator

If the code successfully gets a reference to the coordinator, it goes on to create the context Then, the

code sets the coordinator for the context and returns the context

Trang 3

I hope that the way in which all of the objects in the Core Data stack fi t together is becoming a bit clearer Remember that you did not have to write any code to create the data store, the Persistent Store Coordinator, the Managed Object Model, or the Managed Object Context The template code takes care of all of this for you automatically You will see that the only interface that you need to deal with is that of the Managed Object Context

The Data Model

Before you examine the RootViewController and how to use it to create your TableView using Core Data, let ’ s look at the template Managed Object Model If you open the Resources folder in Xcode and double - click on Tasks.xcdatamodel , you should see something similar to Figure 5 - 3

FIGURE 5 - 3: The default Object Model

The code template creates a default entity called “ Event ” The blue highlighting and resize handles indicate that the Event entity is selected Click anywhere else in the diagram to de - select the Event entity and it will turn pink to indicate that it is no longer selected Click on the Event entity again to select it

There are three panes in the top of the data - modeling tool From left to right they are the Entities pane, the Properties pane, and the Detail pane The Entities pane provides a list of all of the entities

in your model The Properties pane lists the properties of the currently selected entity, and the Detail pane shows details related to whatever is currently selected, either an entity or property The bottom portion of the tool displays a graphical representation of your model called the Diagram View The next chapter provides more detail on using the data - modeling tool

Trang 4

You can see in Figure 5 - 3 that the Event entity has an attribute called timeStamp If you select the

Event entity and then select the timeStamp property in the Properties pane, you will see the details

of the timeStamp attribute in the Detail pane You should see that the timeStamp attribute is

optional and that it is a Date type

Your managed object context will manage the objects defi ned by the Event entity Remember that

when you created the Persistent Store Coordinator, you initialized it with all of the managed object

models in the bundle Then, when you created the context, it used the Persistent Store Coordinator

Next, you will see how you use the Event entity to create and manage Core Data Managed Objects

in code

RootViewController

The RootViewController is a subclass of UITableViewController and contains the TableView

that you will use to display your data This class has a property that holds the context,

which the Application Delegate sets when it creates the RootViewController instance The

RootViewController also has a property that holds an

The NSFetchedResultsController class is the

glue that binds the results of a fetch request against

your datasource to a TableView You will look at the

NSFetchedResultsController class in more detail in

Chapter 6

Figure 5 - 4 shows a high - level view of how the

class takes a fetch request and a context as its inputs

and calls delegate methods when the data in the fetch

request changes The controller implements methods

that you use when implementing the TableView

delegate methods that you are familiar with from

Chapter 3

The fi rst thing to notice is that the RootViewController implements the

NSFetchedResultsControllerDelegate protocol You can see this in the RootViewController.h

header fi le Remember that declaring that you implement a protocol is a contract that commits you

to implementing certain methods Classes that do not implement this protocol cannot be delegates

for an NSFetchedResultsController

The following is the code for the getter method for the fetchedResultsController property:

- (NSFetchedResultsController *)fetchedResultsController {

if (fetchedResultsController != nil) {

return fetchedResultsController;

}

/*

NSFetchedResultsController

Fetched Results Controller Delegate

Context

FIGURE 5 - 4: NSFetchedResultsController usage

Trang 5

Set up the fetched results controller.

*/

// Create the fetch request for the entity.

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];

// Edit the entity name as appropriate.

NSEntityDescription *entity = [NSEntityDescription entityForName:@”Event”

inManagedObjectContext:managedObjectContext];

[fetchRequest setEntity:entity];

// Set the batch size to a suitable number.

[fetchRequest setFetchBatchSize:20];

// Edit the sort key as appropriate.

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@”timeStamp”

ascending:NO];

NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];

[fetchRequest setSortDescriptors:sortDescriptors];

// Edit the section name key path and cache name if appropriate.

// nil for section name key path means “no sections”.

NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc]

initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:@”Root”];

aFetchedResultsController.delegate = self;

self.fetchedResultsController = aFetchedResultsController;

[aFetchedResultsController release];

[fetchRequest release];

[sortDescriptor release];

[sortDescriptors release];

return fetchedResultsController;

}

RootViewController.m

The fi rst part of the code should be familiar It checks to see if you have already created the

fetchedResultsController If it already exists, the method returns the controller If not, the code goes on to create it

The next section of code creates and confi gures the objects needed by the

fetchedResultsController As you can see from Figure 5 - 4, you need a fetch request and a context to be able to use the fetchedResultsController Because you already have a context, in the managedObjectContext property, the code only needs to create a fetch request

You can think of a fetch request as a SQL SELECT statement The code creates a FetchRequest

Trang 6

the fetchRequest Next, the code sets the batch size of the fetchRequest to a reasonable number

of records to receive at a time

The next bit of code creates an NSSortDescriptor You use the NSSortDescriptor to sort

the results in the fetchRequest You can think of the NSSortDescriptor as a SQL ORDER BY

clause Here, you order the result set based on the timeStamp fi eld in descending order The

NSSortDescriptor then sets the sort descriptor used by the fetch request

Finally, calling the initWithFetchRequest:managedObjectContext:sectionNameKeyPath:

cacheName: method creates and initializes fetchedResultsController The template then sets the

delegate to self , assigns the fetchedResultsController property, and then the template releases

the local objects

The only delegate method from the NSFetchedResultsControllerDelegate protocol implemented

by the template is controllerDidChangeContent: The fetchedResultsController calls this

method when all changes to the objects managed by the controller are complete In this case, you

tell the table to reload its data

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {

// In the simplest, most efficient, case, reload the table view.

[self.tableView reloadData];

}

RootViewController.m

Now that you have seen how you create and confi gure the fetchedResultsController , let ’ s look

at how you confi gure the RootViewController at startup You do this in the viewDidLoad method:

- (void)viewDidLoad {

[super viewDidLoad];

// Set up the edit and add buttons.

self.navigationItem.leftBarButtonItem = self.editButtonItem;

UIBarButtonItem *addButton = [[UIBarButtonItem alloc]

initWithBarButtonSystemItem:UIBarButtonSystemItemAdd

target:self

action:@selector(insertNewObject)];

self.navigationItem.rightBarButtonItem = addButton;

[addButton release];

NSError *error = nil;

if (![[self fetchedResultsController] performFetch: & error]) {

// Log the error

NSLog(@”Unresolved error %@, %@”, error, [error userInfo]);

// Quit

abort();

}

}

RootViewController.m

Trang 7

The method fi rst calls the superclass version of viewDidLoad to ensure that you perform any initialization required by the superclass

Next, the code confi gures the Edit button, creates the Add button and adds the buttons to the navigation at the top of the screen You can see in the initialization of the addButton that the insertNewObject method will be called when someone taps the Add button

Finally, you call the performFetch: method on the fetchedResultsController to execute the fetch request and retrieve the desired data

Now, you will look at how you use the TableView delegate methods to display your data These should be familiar to you from Chapter 3

The fi rst method is numberOfSectionsInTableView :

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return [[fetchedResultsController sections] count];

}

RootViewController.m

As you may recall, the TableView calls this method when it needs to know how many sections to display Here, the code simply asks the fetchedResultsController for the number of sections

The next TableView delegate method is numberOfRowsInSection :

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

id < NSFetchedResultsSectionInfo > sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];

return [sectionInfo numberOfObjects];

}

RootViewController.m

Again, you call upon the fetchedResultsController to return the number of rows to display

Finally, you confi gure the cell in the cellForRowAtIndexPath : method

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

static NSString *CellIdentifier = @”Cell”;

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

if (cell == nil) { cell = [[[UITableViewCell alloc]

initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];

}

Trang 8

// Configure the cell.

NSManagedObject *managedObject = [fetchedResultsController

objectAtIndexPath:indexPath];

cell.textLabel.text =

[[managedObject valueForKey:@”timeStamp”] description];

return cell;

}

RootViewController.m

This code should be familiar down to the point where it retrieves the managed object Again, the

code asks fetchedResultsController for the object pointed to by the index path Once it obtains

this object, the code uses key - value coding to get the value for the timeStamp property You learn

more about key - value coding in Chapter 6

The commitEditingStyle:forRowAtIndexPath: method contains the code to handle editing rows

in the TableView The TableView calls this method when editing of the TableView will cause a

change to the underlying data In this case, deleting an object in the TableView should delete the

object from the context

- (void)tableView:(UITableView *)tableView

commitEditingStyle:(UITableViewCellEditingStyle)editingStyle

forRowAtIndexPath:(NSIndexPath *)indexPath {

if (editingStyle == UITableViewCellEditingStyleDelete) {

// Delete the managed object for the given index path

NSManagedObjectContext *context =

[fetchedResultsController managedObjectContext];

[context deleteObject:

[fetchedResultsController objectAtIndexPath:indexPath]];

// Save the context.

NSError *error = nil;

if (![context save: & error]) {

NSLog(@”Unresolved error %@, %@”, error, [error userInfo]);

abort();

}

}

}

RootViewController.m

The code fi rst determines if the user has deleted a cell Then, it gets a reference to the context and

tells the context to delete the object that was deleted from the TableView Last, the context changes

are committed to disk by calling the save: method

The last interesting bit of code in the RootViewController is the insertNewObject method Recall

that this is the method that will be called when a user taps the Add button at the top of the screen

Trang 9

- (void)insertNewObject {

// Create a new instance of the entity managed by the fetched results // controller.

NSManagedObjectContext *context = [fetchedResultsController managedObjectContext];

NSEntityDescription *entity = [[fetchedResultsController fetchRequest] entity];

NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name]

inManagedObjectContext:context];

// If appropriate, configure the new managed object.

[newManagedObject setValue:[NSDate date] forKey:@”timeStamp”];

// Save the context.

NSError *error = nil;

if (![context save: & error]) { NSLog(@”Unresolved error %@, %@”, error, [error userInfo]);

abort();

} }

RootViewController.m

Like the commitEditingStyle:forRowAtIndexPath: method, this code fi rst gets a reference to the context Next, the code creates a new entity based on the entity that the

fetchedResultsController uses The code then creates a new managed object based on that entity and inserts it into the context Next, the code confi gures the managed object with the appropriate data; in this case, a timestamp Finally, the context is committed to disk with the save: method

Modifying the Template Code

Now that you are familiar with how the template code works, you will modify it to create tasks instead of timestamps To build the task application, you will modify the data model by creating

a Task entity, create a new ViewController that you will use to create tasks, and update the

RootViewController to use the new Task entity

The fi rst thing that you will do is to modify the data model by changing the existing Event entity to make it a Task entity If you make a change to the data model and attempt to run your application, you will get an error that looks something like this:

2010-03-31 12:50:34.595 Tasks[2424:207] Unresolved error Error Domain=NSCocoaErrorDomain Code=134100 UserInfo=0x3d12140

“Operation could not be completed (Cocoa error 134100.)”, { metadata = {

NSPersistenceFrameworkVersion = 248;

NSStoreModelVersionHashes = { Location = < fa099c17 c3432901 bbaf6eb3 1dddc734 a9ac14d2 36b913ed 97ebad53 3e2e5363 > ;

Task = < 40414517 78c0bd9f 84e09e2a 91478c44 d85394f8 e9bb7e5a abb9be27 96761c30 > ;

Trang 10

NSStoreModelVersionHashesVersion = 3;

NSStoreModelVersionIdentifiers = (

);

NSStoreType = SQLite;

NSStoreUUID = “762ED962-367C-476C-B4BD-076A6D1C33A9”;

“_NSAutoVacuumLevel” = 2;

};

reason = “The model used to open the store is incompatible with the one

used to create the store”;

}

RootViewController.m

This error says that “ The model used to open the store is incompatible with the one used to create

the store ” This means exactly what it sounds like: The data store on the device is not compatible

with your revised data model, so Core Data cannot open it When you encounter this situation, you

will need to use Core Data migration to move your data from one data model to another Core Data

migration is covered in detail in Chapter 9 For now, simply delete the existing application from the

simulator or device before trying to run it again This will force Core Data to build a new data store

that is compatible with the data model

Open the Tasks.xcdatamodel fi le, and then select the Event entity Change the name of the entity

to “ Task ” in the Detail pane Add a new property to the Task entity by clicking the plus icon below

the Properties pane and selecting Add Attribute Call the attribute taskText and set its type to

String in the Properties pane You will use the new attribute to store the text for your tasks Your

data model should look like Figure 5 - 5

FIGURE 5 - 5: Revised Tasks data model

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

TỪ KHÓA LIÊN QUAN