Saving, Loading, and Application States
Archiving
Archiving类似JAVA的序列化,可以将对象保存到硬盘上,也可加载硬盘上的数据来重新创建对象。
如果一个类要支持归档,需要实现NSCoding protocol,并实现encodeWithCoder: 和initWithCoder:方法.
在BKItem.h中声明确认NSCoding协议。
@interface BKItem : NSObject
在BKItem.m中实现这两个方法:
// archived
- (void)encodeWithCoder:(NSCoder *)aCoder{
[aCoder encodeObject:self.itemName forKey:@"itemName"];
[aCoder encodeObject:self.serialNumber forKey:@"serialNumber"];
[aCoder encodeObject:self.dateCreated forKey:@"dateCreated"];
[aCoder encodeObject:self.itemKey forKey:@"itemKey"];
[aCoder encodeInt:self.valueInDollars forKey:@"valueInDollars"];
}
// unarchived
- (instancetype)initWithCoder:(NSCoder *)aDecoder{
self = [super init];
if (self) {
_itemName = [aDecoder decodeObjectForKey:@"itemName"];
_serialNumber = [aDecoder decodeObjectForKey:@"serialNumber"];
_dateCreated = [aDecoder decodeObjectForKey:@"dateCreated"];
_itemKey = [aDecoder decodeObjectForKey:@"itemKey"];
_valueInDollars = [aDecoder decodeIntForKey:@"valueInDollars"];
}
return self;
}
Application Sandbox
每个应用都有自己的application sandbox,其他应用无法访问你的应用的sandbox。
application sandbox由以下目录组成:
directory | description |
---|---|
application bundle | 只读目录,包含应用的可执行文件以及资源文件,如图片,NIB等 |
Documents/ | 该目录下的内容可以同步到iTunes或iCloud |
Library/Caches/ | 缓存目录,缓存后台服务器的资源数据,即使丢失,也可以从后台再次获取到 |
Library/Preferences/ | application preference,会同步到iTunes或iCloud.NSUserDefaults 处理此目录数据 |
tmp/ | 临时目录,NSTemporaryDirectory 指向此目录 |
将BKItem保存到Documents目录,在BKItemStore.m中添加如下方法:
// 返回application sandbox的Documents目录
- (NSString *)itemArchivePath{
NSArray *documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [documentDirectories firstObject];
return [documentDirectory stringByAppendingPathComponent:@"items.archive"];
}
NSKeyedArchiver and NSKeyedUnarchiver
利用NSKeyedArchiver将数据写到指定路径。
在BKItemStore.h中声明方法来保存数据到文件系统:
- (BOOL)saveChanges;
在BKItemStore.m中实现此方法:
- (BOOL)saveChanges{
NSString *path = [self itemArchivePath];
// 1. 创建NSKeyedArchiver实例对象;
// 2. 调用privateItems对象(NSMutableArray)的encodeWithCoder:方法,形参是NSKeyedArchiver实例;
// 3. 数组向其内部的每个BKItem对象,发送encodeWithCoder:消息,形参是同样的NSKeyedArchiver实例;
// 4. NSKeyedArchiver写数据到指定路径上
return [NSKeyedArchiver archiveRootObject:self.privateItems toFile:path];
}
什么时候来调用此方法呢?
当用户点击home键,会发送applicationDidEnterBackground:消息给App Delegate,在该方法中保存数据到文件系统。
#import "BKItemStore.h"
@implementation BKAppDelegate
- (void)applicationDidEnterBackground:(UIApplication *)application
{
BOOL success = [[BKItemStore sharedStore] saveChanges];
if (success) {
NSLog(@"Saved all of the BKItems.");
} else {
NSLog(@"Could not save any of the BKItems.");
}
}
在BKItemStore.m的初始化方法中,从归档文件中反序列化对象,调用NSKeyedUnarchiver的unarchiveObjectWithFile:方法。
- (instancetype)initPrivate{
self = [super init];
if(self){
// Unarchive data
NSString *path = [self itemArchivePath];
_privateItems = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
if (!_privateItems) {
_privateItems = [[NSMutableArray alloc] init];
}
}
return self;
}
现在可以动态的创建BKItem,保存到文件系统,加载他们。修改BKItemStore.m的createItem方法,不再生成随机值:
- (BKItem *)createItem{
//BKItem *item = [BKItem randomItem];
BKItem *item = [[BKItem alloc] init];
[self.privateItems addObject:item];
return item;
}
Application States and Transitions
下图是应用的各种状态,以及状态切换时执行的方法。
Application states:
State | Visible | Receives Events | Executes Code |
---|---|---|---|
Not Running | No | No | No |
Active | Yes | Yes | Yes |
Inactive | Mostly | No | Yes |
Background | No | No | Yes |
Suspended | No | No | No |
Writing to the Filesystem with NSData
利用NSData将图片写到文件系统。
在BKImageStore.m中添加如下方法,根据图片KEY返回图片路径:
// 根据指定的key, 返回图片的保存路径
- (NSString *)imagePathForKey:(NSString *)key{
NSArray *documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [documentDirectories firstObject];
return [documentDirectory stringByAppendingPathComponent:key];
}
保存图片到文件系统:
- (void)setImage:(UIImage *)image forKey:(NSString *)key{
//[self.dictionary setObject:image forKey:key];
self.dictionary[key] = image;
// 保存图片到文件系统
NSString *imagePath = [self imagePathForKey:key];
NSData *data = UIImageJPEGRepresentation(image, 0.5);
[data writeToFile:imagePath atomically:YES];
}
删除图片:
- (void)deleteImageForKey:(NSString *)key{
if (!key) {
return;
}
[self.dictionary removeObjectForKey:key];
// 删除文件系统上的图片
NSString *imagePath = [self imagePathForKey:key];
[[NSFileManager defaultManager] removeItemAtPath:imagePath error:nil];
}
从文件系统上读取图片:
- (UIImage *)imageForKey:(NSString *)key{
//return [self.dictionary objectForKey:key];
//return self.dictionary[key];
UIImage *result = self.dictionary[key];
if (!result) {
NSString *imagePath = [self imagePathForKey:key];
// Create UIImage object from file
result = [UIImage imageWithContentsOfFile:imagePath];
if (result) {
self.dictionary[key] = result;
} else {
NSLog(@"Error: unable to find %@", [self imagePathForKey:key]);
}
}
return result;
}
NSNotificationCenter and Low-Memory Warnings
每个应用都有一个NSNotificationCenter实例。
当一个对象发送notification,notification center会转会此notification到其observer。
比如当内存过低,notification center会收到UIApplicationDidReceiveMemoryWarningNotification,可以在notification center中添加此notification的observer。
BKImageStore.m中,将其注册为UIApplicationDidReceiveMemoryWarningNotification的observer,当内存过低时,清空图片所占内存。
- (instancetype)initPrivate{
self = [super init];
if (self) {
_dictionary = [[NSMutableDictionary alloc] init];
// 将BKImageStore注册了内存过低notification的observer
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:@selector(clearCache:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
}
return self;
}
- (void)clearCache:(NSNotification *)note{
NSLog(@"flushing %d images out of the cache", [self.dictionary count]);
[self.dictionary removeAllObjects];
}
NSNotification
除了delegation和target-action pairs,notification是另外一种回调。不过delegation和target-action paris会直接发送消息到delegate和target,notification使用middl-man: NSNotificationCenter。
每个NSNotification对象都有name(notification center通过名字查找observer),object(此对象负责发送notification),可选的userInfo dictionary(包含一些poster想让observer知道的信息)。
addObserver:selector:name:object:方法的最后一个参数如果是nil,表示任何对象发送notification,observer都会收到。如果指定了某个对象,则只有当此对象发送notification时,observer才会收到notification。
同一个notification,可以有多个对象是其observer,所以当有多个对象需要响应同一个event时,就需要使用notification。
NSNotificationCenter和push/local notification没有关系。
Reading and Writing to the Filesystem
对于binary data使用NSData,文本使用NSString的两个实例方法writeToFile:atomically:encoding:error: 和initWithContentsOfFile:。除了NSString有这两个方法,NSDictionary 和 NSArray也有,不过他们写到文件系统后生成的是XML文件,而且NSDictionary 和 NSArray中的对象必须是可序列化的(property list serializable)对象:NSString, NSNumber, NSDate, NSData, NSArray, and NSDictionary.。
NSError *err;
NSString *someString = @"Text Data";
BOOL success = [someString writeToFile:@"/some/path/file" atomically:YES encoding:NSUTF8StringEncoding error:&err];
if (success) {
NSLog(@"Error writing file: %@", [err localizedDescription]);
}
NSString *myEssay = [[NSString alloc] initWithContentsOfFile:@"/some/path/file" encoding:NSUTF8StringEncoding error:&err];
if (!myEssay) {
NSLog(@"Error reading file %@", [err localizedDescription]);
}
注意上面代码中的err对象,开始并没有创建error对象,只是声明了一个NSError类型的变量,只有当错误发生时才需要真正创建错误对象。&err
表示变量的内存地址。
UIAlertView
如果要向用户显示错误提示,可以使用UIAlertView对象。
UIAlertView *a = [[UIAlertView alloc] initWithTitle:@"Error" message:[err localizedDescription] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
[a show];
The Application Bundle
在Build Phases中查看application bundle。
在app在模拟器上运行后,查看~/Library/Application Support/iPhone Simulator/ (version number)/Applications
目录,可以看到application sandbox,点击 查看包内容。
可以读取application bundle中的文件,但是不能修改和添加文件:
// Get a pointer to the application bundle
NSBundle *applicationBundle = [NSBundle mainBundle];
// Ask for the path to a resource named myImage.png in the bundle
NSString *path = [applicationBundle pathForResource:@"myImage" ofType:@"png"];
本文是对《iOS Programming The Big Nerd Ranch Guide 4th Edition》第十八章的总结。
最近又被派到到JAVAEE项目了,而且开始加班,看书的时间越来越少了。