備忘錄模式捕獲和具體化對象的內部狀態。換句話說,它可以節省你的東西後來,這種外部狀態可以恢復在不違反封裝;
也就是說,私人數據是私有的。
怎麼用備忘錄設計模式
在Viewcontroller的實現文件裡添加下面方法:
- (void)saveCurrentState
{
// When the user leaves the app and then comes back again, he wants it to be in the exact same state
// he left it. In order to do this we need to save the currently displayed album.
// Since it's only one piece of information we can use NSUserDefaults.
[[NSUserDefaults standardUserDefaults] setInteger:currentAlbumIndex forKey:@"currentAlbumIndex"];
}
- (void)loadPreviousState
{
currentAlbumIndex = [[NSUserDefaults standardUserDefaults] integerForKey:@"currentAlbumIndex"];
[self showDataForAlbumAtIndex:currentAlbumIndex];
}
saveCurrentState 保存了這個當前專輯的索引用NSUserDefaults 他是一個蘋果提供的標准的存儲應用設置和數據的。
loadPreviousState 加載之前保存的索引。這不是備忘錄設計模式的完全實現。
現在添加代碼在滑動視圖初始化前面:
[self loadPreviousState];
應用程序啟動時加載以前保存的狀態,但是你保存應用當前轉台只是為了加載?你將要用通知做這件事。當應用進入到後台系統發送
UIApplicationDidEnterBackgroundNotification。你可以用這個通知調用saveCurrentState。這不是很方面嗎?
在ViewDidLoad添加下面代碼:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(saveCurrentState) name:UIApplicationDidEnterBackgroundNotification object:nil];
現在這個應用進入到後台。將要自動的調用saveCurrentState方法來保存當前狀態。
現在添加下面代碼:
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
這個保證你Viewcontroller銷毀的時候移除這個類注冊的觀察者。
構建和運行您的應用程序。導航到一個專輯,將應用程序發送到後台使用命令+ Shift + H(如果你是在模擬器),然後關閉應用程序,重新啟動,並檢查之前,選擇專輯為中心:
看起來你的專輯數據是正確的。但是你的滑動視圖沒有在中間這是怎麼了?
這就是上面可選協議方法的意義
initialViewIndexForHorizontalScroller:因為這個方法在代理類裡面沒有實現。在這個例子代理類就是Viewcontroller,因此初始視圖一直被設置為第一個視圖。
為了修正他在Viewcontroller的實現文件裡面添加下面代碼:
- (NSInteger)initialViewIndexForHorizontalScroller:(HorizontalScroller *)scroller
{
return currentAlbumIndex;
}
現在這個滑動視圖的第一個視圖可以設置為任何專輯是由
currentAlbumIndex表明的。這是一個很好的方法來確保你的應用體驗來維持私有和可恢復的。
如果你看看PersistencyManager的init,你將要注意到這個專輯數據是硬件編碼並且PersistencyManager創建的時候都會重新創建。最好的解決方法是一旦創建專輯列表就把他們存儲到一個文件。那麼怎麼把專輯存儲到文件呢?
一種方法是迭代專輯的屬性,保存他們到一個plist文件並且然後創建專輯的實例一旦需要的時候。但是這不是最佳選擇,由於需要你編寫特定的代碼依靠每個類的屬性和數據。例如你接下來創建的movies類有不同的屬性,保存和加載數據需要新的代碼。
此外,你不能保存每個類的實例的私有變量,因為他們無法訪問外部類。這就是為什麼蘋果公司創建歸檔機制。
歸檔
蘋果的一個專門記憶模式的實現是歸檔。把一個對象轉化為流便於保存和恢復不用向外部類暴露私有的屬性。你可以參考Apple’s Archives and Serializations Programming Guide.
那麼怎麼用歸檔呢?
首先你需要聲明專輯通過符合NSCoding協議是可以歸檔的。在專輯頭文件寫上符合NSCoding協議:
@interface Album : NSObject <NSCoding>
在實現文件裡面:
添加兩個方法:
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.year forKey:@"year"];
[aCoder encodeObject:self.title forKey:@"album"];
[aCoder encodeObject:self.artist forKey:@"artist"];
[aCoder encodeObject:self.coverUrl forKey:@"cover_url"];
[aCoder encodeObject:self.genre forKey:@"genre"];
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self)
{
_year = [aDecoder decodeObjectForKey:@"year"];
_title = [aDecoder decodeObjectForKey:@"album"];
_artist = [aDecoder decodeObjectForKey:@"artist"];
_coverUrl = [aDecoder decodeObjectForKey:@"cover_url"];
_genre = [aDecoder decodeObjectForKey:@"genre"];
}
return self;
}
你調用encodeWithCoder:當你歸檔你類的一個實例時候,
相反地的當你解檔一個實例來創建一個專輯的實例。看著簡單而且強大。現在專輯類可以被歸檔了。
添加的代碼實際上保存和加載相冊的列表。
添加以下簽名(或方法原型)PersistencyManager.h:
這將是叫的方法保存相冊。
現在,添加PersistencyManager.m方法實現:
- (void)saveAlbums
{
NSString *filename = [NSHomeDirectory() stringByAppendingString:@"/Documents/albums.bin"];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:albums];
[data writeToFile:filename atomically:YES];
}
NSKeyedArchiver檔案專輯數組名為albums.bin的文件。
當你歸檔對象包含其他對象,
歸檔器會自動的迭代歸檔子對象並且孩子的任何子對象。
在此情況下開始對專輯類歸檔,這是專輯實例的一個數組,因此數組和專輯都支持NSCoding接口。任何在數組裡面的東西都會被歸檔。
現在在
PersistencyManager.m init方法裡添加下面代碼:
- (id)init
{
self = [super init];
if (self) {
NSData *data = [NSData dataWithContentsOfFile:[NSHomeDirectory() stringByAppendingString:@"/Documents/albums.bin"]];
albums = [NSKeyedUnarchiver unarchiveObjectWithData:data];
if (albums == nil)
{
albums = [NSMutableArray arrayWithArray:
@[[[Album alloc] initWithTitle:@"Best of Bowie" artist:@"David Bowie" coverUrl:@"http://www.coversproject.com/static/thumbs/album/album_david%20bowie_best%20of%20bowie.png" year:@"1992"],
[[Album alloc] initWithTitle:@"It's My Life" artist:@"No Doubt" coverUrl:@"http://www.coversproject.com/static/thumbs/album/album_no%20doubt_its%20my%20life%20%20bathwater.png" year:@"2003"],
[[Album alloc] initWithTitle:@"Nothing Like The Sun" artist:@"Sting" coverUrl:@"http://www.coversproject.com/static/thumbs/album/album_sting_nothing%20like%20the%20sun.png" year:@"1999"],
[[Album alloc] initWithTitle:@"Staring at the Sun" artist:@"U2" coverUrl:@"http://www.coversproject.com/static/thumbs/album/album_u2_staring%20at%20the%20sun.png" year:@"2000"],
[[Album alloc] initWithTitle:@"American Pie" artist:@"Madonna" coverUrl:@"http://www.coversproject.com/static/thumbs/album/album_madonna_american%20pie.png" year:@"2000"]]];
[self saveAlbums];
}
}
return self;
}
在新的代碼裡,
NSKeyedUnarchiver 從文件裡面加載新的專輯數據,如果他存在。如果他不存在,
立即創建相冊數據和保存它在下次加載應用程序。
你還想保存這張專輯每次應用程序進入後台數據。
這可能不是必要的,但如果你後來添加的選項更改相冊數據
那麼你想要確保您已經保存了所有更改。
添加以下LibraryAPI.h方法簽名:
- (void)saveAlbums;
應用通過libraryAPI訪問所有的服務,這就是應用怎麼讓PersitencyManager 知道他需要保存專輯的數據。
現在添加這個方法實現到libraryAPI實現文件裡:
- (void)saveAlbums
{
[persistencyManager saveAlbums];
}
這段代碼僅僅通過在調用LibraryAPI保存相冊到PersistencyMangaer
。和上面的代碼使用LibraryAPI觸發保存專輯的數據每當Viewcontroller保存其狀態。
編譯運行你的應用檢查是不是所有東西運行正常。
不幸的是,沒有簡單的方法來檢查數據持久性是正確的。幸運的是你可以在finder裡檢查模擬器的document文件夾看專輯的數據文件是不是被創建,但是為了檢查是不是有改變你不得不添加改變專輯數據的功能。
但是代替改變數據,如果你添加一個選項來刪除你的專輯當你不想要的時候。此外你應該有一個撤銷的選項一旦你誤刪了專輯。