首页 » iOS编程(第4版) » iOS编程(第4版)全文在线阅读

《iOS编程(第4版)》18.3 NSKeyedArchiver与NSKeyedUnarchiver

关灯直达底部

18.2节修改了BNRItem类,能使Homepwner固化BNRItem对象。此外,还介绍了保存数据所需的目录。最后要解决的两个问题是:①如何保存或读取数据?②何时保存或读取数据?下面先解决“保存”问题。Homepwner应该在退出(exit)时,通过NSKeyedArchiver类保存BNRItem对象。

在BNRItemStore.h中声明一个新方法,代码如下:

- (BOOL)saveChanges;

在BNRItemStore.m中实现该方法,向NSKeyedArchiver类发送archiveRootObject: toFile:消息,代码如下:

- (BOOL)saveChanges

{

NSString *path = [self itemArchivePath];

// 如果固化成功就返回YES

return [NSKeyedArchiver archiveRootObject:self.privateItems

toFile:path];

}

这段代码中的archiveRootObject:toFile:会将privateItems中的所有BNRItem对象都保存至路径为itemArchivePath的文件。代码本身很简单,其工作原理如下:

•archiveRootObject:toFile:会先创建一个NSKeyedArchiver对象。(NSKeyed- Archiver是抽象类NSCoder的具体实现子类。)

•然后,archiveRootObject:toFile:会向privateItems发送encodeWithCoder:消息,并传入NSKeyedArchiver对象作为第一个参数。

•privateItems的encodeWithCoder:方法会向其包含的所有BNRItem对象发送encodeWithCoder:消息,并传入同一个NSKeyedArchiver对象。这些BNRItem对象都会将其属性编码至同一个NSKeyedArchiver对象(见图18-4)。

•当所有的对象都完成编码后,NSKeyedArchiver对象就会将数据写入指定的文件。

图18-4 固化privateItems中包含的BNRItem对象

在用户按下设备的主屏幕按钮后,BNRAppDelegate对象会收到application- DidEnterBackground:消息。Homepwner应该在applicationDidEnterBackground:中向BNRItemStore对象发送saveChanges消息。

在BNRAppDelegate.m顶部导入BNRItemStore.h,然后实现applicationDid- EnterBackground:,保存所有的BNRItem对象,代码如下:

#import “BNRItemStore.h”

@implementation HomepwnerAppDelegate

- (void)applicationDidEnterBackground:(UIApplication *)application

{

BOOL success = [[BNRItemStore sharedStore] saveChanges];

if (success) {

NSLog(@“Saved all of the BNRItems”);

} else {

NSLog(@“Could not save any of the BNRItems”);

}

}

(Xcode在创建Homepwner项目时,可能已经通过模板为BNRAppDelegate实现了applicationDidEnterBackground:。如果BNRAppDelegate.m已经有了一个名为applicationDidEnterBackground:的方法,就应该在已有方法的尾部增加上述代码,而不是重复加入同一个方法。)

针对模拟器构建并运行应用。增加若干BNRItem对象,然后按下主屏幕按钮,退出至主屏幕。Homepwner应该会在控制台输出相应的提示信息,表示已经保存了全部BNRItem对象。

虽然Homepwner现在还不能读取之前保存的文件并还原BNRItem对象,但是可以验证应用是否将某些数据保存至了指定的文件。在Finder中,使用快捷键Command-Shift-G,然后在文本框中输入~/Library/ApplicationSupport/iPhoneSimulator,最后单击“前往”(Go)按钮。Finder会打开指定的目录,模拟器会在该目录中保存所有已安装的应用和相应的程序包。

根据构建并运行应用时所指定的iOS版本(这里以iOS 7.0为例),打开相应的目录(7.0)。打开Applications目录,应该可以看到安装至模拟器的全部应用。因为这些目录的目录名并没有包含应用的名称,所以只能逐个打开查找,直到找到包含Homepwner应用的那个。

在包含Homepwner的目录中,打开Documents目录(见图18-5)。Finder应该会显示一个名为items.archive的文件。建议读者为iPhone Simulator目录创建一个“快捷方式”(alias),方便将来再次访问。

图18-5 Homepwner的沙盒

下面继续为Homepwner添加功能,使之能够读取前面保存的文件。为了能在Homepwner启动时载入之前保存的全部BNRItem对象,需要在创建BNRItemStore对象时使用NSKeyedUnarchiver类。在BNRItemStore.m中,将以下代码加入initPrivate。

- (instancetype)initPrivate

{

self = [super init];

if (self) {

_privateItems = [[NSMutableArray alloc] init];

NSString *path = [self itemArchivePath];

_privateItems = [NSKeyedUnarchiver unarchiveObjectWithFile:path];

// 如果之前没有保存过privateItems,就创建一个新的

if (!_privateItems) {

_privateItems = [[NSMutableArray alloc] init];

}

}

return self;

}

这段代码中的unarchiveObjectWithFile:类方法会创建一个NSKeyedUnarchiver对象,然后根据指定的路径载入固化文件。接着,NSKeyedUnarchiver类会查看固化文件中的根对象,然后根据根对象的类型创建相应的对象。Homepwner在创建固化文件时,使用的根对象是NSMutableArray对象,所以解固时的根对象也是NSMutableArray(如果根对象是BNRItem对象,那么unarchiveObjectWithFile:也会返回BNRItem对象)。

创建完NSMutableArray对象后,NSKeyedUnarchiver的unarchiveObjectWithFile:方法会向新创建的NSMutableArray对象发送initWithCoder:消息,并将NSKeyedUnarchiver对象作为实参传入。NSMutableArray对象会通过NSKeyedUnarchiver对象解码相关的对象(BNRItem对象),向所有解固后的对象发送initWithCoder:消息,传入同一个NSKeyedUnarchiver对象。

构建并运行应用,增加若干BNRItem对象,然后终止Homepwner。再次运行Homepwner,应该会看到之前创建的BNRItem对象。测试Homepwner的保存与读取功能时要注意:如果是通过单击Xcode的Stop按钮来终止Homepwner的,那么Homepwner将没有机会保存BNRItem对象。所以读者必须先按下主屏幕按钮,然后再单击Xcode的Stop按钮。

之前因为无法保存BNRItem对象,所以Homepwner创建的都是随机的BNRItem对象,以方便测试。更新后的Homepwner已经支持保存并读取BNRItem对象,所以没有必要再创建随机的BNRItem对象。更新BNRItemStore.m中的createItem方法,修改为创建空的BNRItem对象,代码如下:

- (BNRItem *)createItem

{

BNRItem *item = [BNRItem randomItem];

BNRItem *item = [[BNRItem alloc] init];

[self.privateItems addObject:item];

return item;

}