Homepwner虽然可以固化所有的BNRItem对象,但是并不会保存相关的图片。下面扩充BNRImageStore,实现功能:加入图片时将图片保存为文件;需要时再从文件载入图片。
BNRItem对象的图片应该保存至Documents目录。保存文件时,可以将BNRItem对象的itemKey属性(用户拍摄或选取图片时生成)作为图片文件的文件名。
打开BNRImageStore.m,在类扩展中加入以下方法声明。
- (NSString *)imagePathForKey:(NSString *)key;
在BNRImageStore.m中实现imagePathForKey:,根据传入的键创建相应的文件路径。
- (NSString *)imagePathForKey:(NSString *)key
{
NSArray *documentDirectories =
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask,
YES);
NSString *documentDirectory = [documentDirectories firstObject];
return [documentDirectory stringByAppendingPathComponent:key];
}
为了保存图片,需要先将图片的数据按JPEG格式提取出来,然后拷贝至内存中的某块缓冲区。Foundation框架提供的NSData类可以创建、维护和释放内存缓冲区,所以无须使用malloc这类C函数。NSData对象可以保存一定字节数的二进制数据,下面通过NSData来保存图片数据。
修改BNRImageStore.m中的setImage:forKey:,获取图片路径并保存图片,代码如下:
- (void)setImage:(UIImage *)image forKey:(NSString *)key
{
self.dictionary[key] = image;
// 获取保存图片的全路径
NSString *imagePath = [self imagePathForKey:key];
// 从图片提取JPEG格式的数据
NSData *data = UIImageJPEGRepresentation(image, 0.5);
// 将JPEG格式的数据写入文件
[data writeToFile:imagePath atomically:YES];
}
UIImageJPEGRepresentation函数有两个实参,一个是UIImage对象,另一个是浮点数变量,代表压缩质量。压缩质量的值必须在0到1之间,1代表最高质量(不压缩)。该函数会返回一个NSData对象。
向某个NSData对象发送writeToFile:atomically:消息,可以将该对象中的数据写入指定的文件。writeToFile:atomically:的第一个实参负责指定文件路径。第二个实参atomically是一个布尔值,当atomically为YES时,NSData对象会先将数据写入某个临时文件,然后等写入操作成功后再将文件移至第一个实参所指定的路径,并覆盖已有的文件。这样,即使应用在写入文件的过程中崩溃,也不会损坏现有的数据。
需要注意的是,这种将数据写入文件的方式不是固化。虽然NSData对象自身也可以固化,但writeToFile:atomically:的工作原理是将NSData对象中的数据逐字节复制到文件中。
修改BNRImageStore.m中的deleteImageForKey:,在移除指定的UIImage对象后,删除相应的图片文件。
- (void)deleteImageForKey:(NSString *)key
{
if (!key) {
return;
}
[self.dictionary removeObjectForKey:key];
NSString *imagePath = [self imagePathForKey:key];
[[NSFileManager defaultManager] removeItemAtPath:imagePath
error:nil];
}
修改后的BNRImageStore对象会将所有的图片保存在各自的文件中。当ImageStore对象要返回某个BNRItem对象的图片时,就需要读取相应的文件。通过UIImage的类方法imageWithContentsOfFile:可以从指定的文件载入图片。
修改BNRImageStore.m中的imageForKey:,使BNRImageStore对象能够通过文件创建图片(如果BNRImageStore对象已经包含指定的图片,就直接返回该图片)。
- (UIImage *)imageForKey:(NSString *)key
{
return self.dictionary[key];
// 先尝试通过字典对象获取图片
UIImage *result = self.dictionary[key];
if (!result) {
NSString *imagePath = [self imagePathForKey:key];
// 通过文件创建UIImage对象
result = [UIImage imageWithContentsOfFile:imagePath];
// 如果能够通过文件创建图片,就将其放入缓存
if (result) {
self.dictionary[key] = result;
}
else {
NSLog(@“Error: unable to find %@”, [self imagePathForKey:key]);
}
}
return result;
}
构建并运行应用,为某个BNRItem对象拍摄一张照片,然后按下Home键。再次启动Homepwner,选中之前设置了照片的BNRItem对象,BNRDetailViewController对象的视图应该会显示所有已存的详细信息,包括之前拍摄的照片。
Homepwner会在用户拍摄照片后立刻将相应的图片存入文件,但是对于BNRItem对象中的其他详细信息,则会在应用进入后台运行状态时再保存。之所以要尽快地保存图片,是因为图片的尺寸很大,会占用太多的存储空间。