Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Beginning iOS5 Development.pdf
Скачиваний:
7
Добавлен:
09.05.2015
Размер:
15.6 Mб
Скачать

456

CHAPTER 13: Basic Data Persistence

[array addObject:field1.text]; [array addObject:field2.text]; [array addObject:field3.text]; [array addObject:field4.text];

[array writeToFile:[self dataFilePath] atomically:YES];

}

This method is pretty simple. We create a mutable array, add the text from each of the four fields to the array, and then write the contents of that array out to a plist file. That’s all there is to saving our data using property lists.

That wasn’t too bad, was it? When our main view is finished loading, we look for a plist file. If it exists, we copy data from it into our text fields. Next, we register to be notified when the application becomes inactive (either by being quit or pushed to the background). When that happens, we gather the values from our four text fields, stick them in a mutable array, and write that mutable array to a property list.

Why don’t you compile and run the application? It should build and then launch in the simulator. Once it comes up, you should be able to type into any of the four text fields. When you’ve typed something in them, press the home button (the circular button with the rounded square in it at the bottom of the simulator window). It’s very important that you press the home button. If you just exit the simulator, that’s the equivalent of forcibly quitting your application. In that case, you will never receive the notification that the application is terminating, and your data will not be saved.

NOTE: Starting in iOS 4, pressing the home button doesn’t typically quit the app—at least not at first. The app is put into a background state, ready to be instantly reactivated in case the user switches back to it. We’ll dig into the details of these states and their implications for running and quitting apps in Chapter 15. In the meantime, if you want to verify that the data really was

saved, you can quit the iPhone simulator entirely, and then restart your app from Xcode. Quitting the simulator is basically the equivalent of rebooting an iPhone, so when it starts up again, your

app will have a fresh relaunch experience.

Property list serialization is pretty cool and easy to use. However, it’s a little limiting, since only a small selection of objects can be stored in property lists. Let’s look at a somewhat more robust approach.

Archiving Model Objects

In the last part of Chapter 9, when we built the Presidents data model object, you saw an example of the process of loading archived data using NSCoder. In the Cocoa world, the term archiving refers to another form of serialization, but it’s a more generic type that any object can implement. Any model object specifically written to hold data should support archiving. The technique of archiving model objects lets you easily write complex objects to a file and then read them back in.

www.it-ebooks.info

CHAPTER 13: Basic Data Persistence

457

As long as every property you implement in your class is either a scalar, like int or float, or an instance of a class that conforms to the NSCoding protocol, you can archive your objects completely. Since most Foundation and Cocoa Touch classes capable of storing data do conform to NSCoding (though there are a few noteworthy exceptions, such as UIImage), archiving is actually relatively easy to implement for most classes.

Although not strictly required to make archiving work, another protocol should be implemented along with NSCoding: the NSCopying protocol, which is a protocol that allows your object to be copied. Being able to copy an object gives you a lot more flexibility when using data model objects. For example, in the Presidents application in Chapter 9, instead of that complex code we wrote to store changes the user made so we could handle both the Cancel and Save buttons, we could have made a copy of the president object and stored the changes in that copy. If the user tapped Save, we would just copy the changed version over to replace the original version.

Conforming to NSCoding

The NSCoding protocol declares two methods, which are both required. One encodes your object into an archive; the other one creates a new object by decoding an archive. Both methods are passed an instance of NSCoder, which you work with in very much the same way as NSUserDefaults, introduced in the previous chapter. You can encode and decode both objects and native datatypes like int and float values using key-value coding.

A method to encode an object might look like this:

- (void)encodeWithCoder:(NSCoder *)encoder { [encoder encodeObject:foo forKey:kFooKey]; [encoder encodeObject:bar forKey:kBarKey]; [encoder encodeInt:someInt forKey:kSomeIntKey];

[encoder encodeFloat:someFloat forKey:kSomeFloatKey]

}

To support archiving in our object, we need to encode each of our instance variables into encoder using the appropriate encoding method. We need to implement a method that initializes an object from an NSCoder, allowing us to restore an object that was previously archived. If you are subclassing a class that also conforms to NSCoding, you need to make sure you call encodeWithCoder: on your superclass, meaning your method would look like this instead:

- (void)encodeWithCoder:(NSCoder *)encoder { [super encodeWithCoder:encoder];

[encoder encodeObject:foo forKey:kFooKey]; [encoder encodeObject:bar forKey:kBarKey]; [encoder encodeInt:someInt forKey:kSomeIntKey];

[encoder encodeFloat:someFloat forKey:kSomeFloatKey]

}

Implementing the initWithCoder: method is slightly more complex than implementing encodeWithcoder:. If you are subclassing NSObject directly, or subclassing some other

www.it-ebooks.info

458

CHAPTER 13: Basic Data Persistence

class that doesn’t conform to NSCoding, your method would look something like the following:

- (id)initWithCoder:(NSCoder *)decoder { if (self = [super init]) {

foo = [decoder decodeObjectForKey:kFooKey]; bar = [decoder decodeObjectForKey:kBarKey];

someInt = [decoder decodeIntForKey:kSomeIntKey]; someFloat = [decoder decodeFloatForKey:kAgeKey];

}

return self;

}

The method initializes an object instance using [super init]. If that’s successful, it sets its properties by decoding values from the passed-in instance of NSCoder. When implementing NSCoding for a class with a superclass that also conforms to NSCoding, the initWithCoder: method needs to look slightly different. Instead of calling init on super, it needs to call initWithCoder:, like so:

- (id)initWithCoder:(NSCoder *)decoder {

if (self = [super initWithCoder:decoder]) {

foo = [decoder decodeObjectForKey:kFooKey]; bar = [decoder decodeObjectForKey:kBarKey];

someInt = [decoder decodeIntForKey:kSomeIntKey]; someFloat = [decoder decodeFloatForKey:kAgeKey];

}

return self;

}

And that’s basically it. As long as you implement these two methods to encode and decode all of your object’s properties, your object is archivable and can be written to and read from archives.

Implementing NSCopying

As we mentioned earlier, conforming to NSCopying is a very good idea for any data model objects. NSCopying has one method, called copyWithZone:, which allows objects to be copied. Implementing NSCopying is similar to implementing initWithCoder:. You just need to create a new instance of the same class, and then set all of that new instance’s properties to the same values as this object’s properties. Here’s what a copyWithZone: method might look like:

- (id)copyWithZone:(NSZone *)zone {

MyClass *copy = [[[self class] allocWithZone:zone] init]; copy.foo = [self.foo copyWithZone:zone];

copy.bar = [self.bar copyWithZone:zone]; copy.someInt = self.someInt; copy.someFloat = self.someFloat;

return copy;

}

www.it-ebooks.info

CHAPTER 13: Basic Data Persistence

459

NOTE: Don’t worry too much about the NSZone parameter. This pointer is to a struct that is used by the system to manage memory. Only in rare circumstances did developers ever need to worry about zones or create their own, and nowadays, it’s almost unheard of to have multiple zones. Calling copy on an object is the same as calling copyWithZone: using the default zone,

which is almost always what you want.

Archiving and Unarchiving Data Objects

Creating an archive from an object or objects that conforms to NSCoding is relatively easy. First, we create an instance of NSMutableData to hold the encoded data, and then we create an NSKeyedArchiver instance to archive objects into that NSMutableData instance:

NSMutableData *data = [[NSMutableData alloc] init]; NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc]

initForWritingWithMutableData:data];

After creating both of those, we then use key-value coding to archive any objects we wish to include in the archive, like this:

[archiver encodeObject:myObject forKey:@"keyValueString"];

Once we’ve encoded all the objects we want to include, we just tell the archiver we’re finished, and write the NSMutableData instance to the file system:

[archiver finishEncoding];

BOOL success = [data writeToFile:@"/path/to/archive" atomically:YES];

If anything went wrong while writing the file, success will be set to NO. If success is YES, the data was successfully written to the specified file. Any objects created from this archive will be exact copies of the objects that were last written into the file.

To reconstitute objects from the archive, we go through a similar process. We create an NSData instance from the archive file and create an NSKeyedUnarchiver to decode the data:

NSData *data = [[NSData alloc] initWithContentsOfFile:path]; NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc]

initForReadingWithData:data];

After that, we read our objects from the unarchiver using the same key that we used to archive the object:

self.object = [unarchiver decodeObjectForKey:@"keyValueString"];

Finally, we tell the archiver we are finished:

[unarchiver finishDecoding];

If you’re feeling a little overwhelmed by archiving, don’t worry. It’s actually fairly straightforward. We’re going to retrofit our Persistence application to use archiving, so you’ll get to see it in action. Once you’ve done it a few times, archiving will become

www.it-ebooks.info

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]