Saving, Loading, and Application States
Archiving
Archiving類似JAVA的序列化,可以將對(duì)象保存到硬盤上,也可加載硬盤上的數(shù)據(jù)來(lái)重新創(chuàng)建對(duì)象。
如果一個(gè)類要支持歸檔,需要實(shí)現(xiàn)NSCoding protocol,并實(shí)現(xiàn)encodeWithCoder: 和initWithCoder:方法.
在BKItem.h中聲明確認(rèn)NSCoding協(xié)議。
@interface BKItem : NSObject <NSCoding>
在BKItem.m中實(shí)現(xiàn)這兩個(gè)方法:
// 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
每個(gè)應(yīng)用都有自己的application sandbox,其他應(yīng)用無(wú)法訪問你的應(yīng)用的sandbox。

application sandbox由以下目錄組成:
| directory | description |
|---|---|
| application bundle | 只讀目錄,包含應(yīng)用的可執(zhí)行文件以及資源文件,如圖片,NIB等 |
| Documents/ | 該目錄下的內(nèi)容可以同步到iTunes或iCloud |
| Library/Caches/ | 緩存目錄,緩存后臺(tái)服務(wù)器的資源數(shù)據(jù),即使丟失,也可以從后臺(tái)再次獲取到 |
| Library/Preferences/ | application preference,會(huì)同步到iTunes或iCloud.NSUserDefaults處理此目錄數(shù)據(jù) |
| tmp/ | 臨時(shí)目錄,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將數(shù)據(jù)寫到指定路徑。
在BKItemStore.h中聲明方法來(lái)保存數(shù)據(jù)到文件系統(tǒng):
- (BOOL)saveChanges;
在BKItemStore.m中實(shí)現(xiàn)此方法:
- (BOOL)saveChanges{
NSString *path = [self itemArchivePath];
// 1. 創(chuàng)建NSKeyedArchiver實(shí)例對(duì)象;
// 2. 調(diào)用privateItems對(duì)象(NSMutableArray)的encodeWithCoder:方法,形參是NSKeyedArchiver實(shí)例;
// 3. 數(shù)組向其內(nèi)部的每個(gè)BKItem對(duì)象,發(fā)送encodeWithCoder:消息,形參是同樣的NSKeyedArchiver實(shí)例;
// 4. NSKeyedArchiver寫數(shù)據(jù)到指定路徑上
return [NSKeyedArchiver archiveRootObject:self.privateItems toFile:path];
}
什么時(shí)候來(lái)調(diào)用此方法呢?
當(dāng)用戶點(diǎn)擊home鍵,會(huì)發(fā)送applicationDidEnterBackground:消息給App Delegate,在該方法中保存數(shù)據(jù)到文件系統(tǒng)。
#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的初始化方法中,從歸檔文件中反序列化對(duì)象,調(diào)用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;
}
現(xiàn)在可以動(dòng)態(tài)的創(chuàng)建BKItem,保存到文件系統(tǒng),加載他們。修改BKItemStore.m的createItem方法,不再生成隨機(jī)值:
- (BKItem *)createItem{
//BKItem *item = [BKItem randomItem];
BKItem *item = [[BKItem alloc] init];
[self.privateItems addObject:item];
return item;
}
Application States and Transitions
下圖是應(yīng)用的各種狀態(tài),以及狀態(tài)切換時(shí)執(zhí)行的方法。
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將圖片寫到文件系統(tǒng)。
在BKImageStore.m中添加如下方法,根據(jù)圖片KEY返回圖片路徑:
// 根據(jù)指定的key, 返回圖片的保存路徑
- (NSString *)imagePathForKey:(NSString *)key{
NSArray *documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [documentDirectories firstObject];
return [documentDirectory stringByAppendingPathComponent:key];
}
保存圖片到文件系統(tǒng):
- (void)setImage:(UIImage *)image forKey:(NSString *)key{
//[self.dictionary setObject:image forKey:key];
self.dictionary[key] = image;
// 保存圖片到文件系統(tǒng)
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];
// 刪除文件系統(tǒng)上的圖片
NSString *imagePath = [self imagePathForKey:key];
[[NSFileManager defaultManager] removeItemAtPath:imagePath error:nil];
}
從文件系統(tǒng)上讀取圖片:
- (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
每個(gè)應(yīng)用都有一個(gè)NSNotificationCenter實(shí)例。
當(dāng)一個(gè)對(duì)象發(fā)送notification,notification center會(huì)轉(zhuǎn)會(huì)此notification到其observer。
比如當(dāng)內(nèi)存過低,notification center會(huì)收到UIApplicationDidReceiveMemoryWarningNotification,可以在notification center中添加此notification的observer。
BKImageStore.m中,將其注冊(cè)為UIApplicationDidReceiveMemoryWarningNotification的observer,當(dāng)內(nèi)存過低時(shí),清空?qǐng)D片所占內(nèi)存。
- (instancetype)initPrivate{
self = [super init];
if (self) {
_dictionary = [[NSMutableDictionary alloc] init];
// 將BKImageStore注冊(cè)了內(nèi)存過低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是另外一種回調(diào)。不過delegation和target-action paris會(huì)直接發(fā)送消息到delegate和target,notification使用middl-man: NSNotificationCenter。
每個(gè)NSNotification對(duì)象都有name(notification center通過名字查找observer),object(此對(duì)象負(fù)責(zé)發(fā)送notification),可選的userInfo dictionary(包含一些poster想讓observer知道的信息)。
addObserver:selector:name:object:方法的最后一個(gè)參數(shù)如果是nil,表示任何對(duì)象發(fā)送notification,observer都會(huì)收到。如果指定了某個(gè)對(duì)象,則只有當(dāng)此對(duì)象發(fā)送notification時(shí),observer才會(huì)收到notification。
同一個(gè)notification,可以有多個(gè)對(duì)象是其observer,所以當(dāng)有多個(gè)對(duì)象需要響應(yīng)同一個(gè)event時(shí),就需要使用notification。
NSNotificationCenter和push/local notification沒有關(guān)系。
Reading and Writing to the Filesystem
對(duì)于binary data使用NSData,文本使用NSString的兩個(gè)實(shí)例方法writeToFile:atomically:encoding:error: 和initWithContentsOfFile:。除了NSString有這兩個(gè)方法,NSDictionary 和 NSArray也有,不過他們寫到文件系統(tǒng)后生成的是XML文件,而且NSDictionary 和 NSArray中的對(duì)象必須是可序列化的(property list serializable)對(duì)象: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對(duì)象,開始并沒有創(chuàng)建error對(duì)象,只是聲明了一個(gè)NSError類型的變量,只有當(dāng)錯(cuò)誤發(fā)生時(shí)才需要真正創(chuàng)建錯(cuò)誤對(duì)象。&err表示變量的內(nèi)存地址。
UIAlertView
如果要向用戶顯示錯(cuò)誤提示,可以使用UIAlertView對(duì)象。
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在模擬器上運(yùn)行后,查看~/Library/Application Support/iPhone Simulator/ (version number)/Applications目錄,可以看到application sandbox,點(diǎn)擊 查看包內(nèi)容。
可以讀取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"];
本文是對(duì)《iOS Programming The Big Nerd Ranch Guide 4th Edition》第十八章的總結(jié)。
最近又被派到到JAVAEE項(xiàng)目了,而且開始加班,看書的時(shí)間越來(lái)越少了。