何為備忘錄模式?
在響應某些事件時,應用程式需要儲存自身的狀態,比如當使用者儲存文檔或程式退出時。例如,遊戲退出之前,可能需要儲存當前會話的狀態,如遊戲等級、敵人數量、可用武器的種類等。遊戲再次開啟時,玩家可以從離開的地方接著玩。很多時候,儲存程式的狀態真的不需要什麼特別巧妙的方法。任何簡單有效方法都可以,但是同時,儲存資訊應該只對原始程式有意義。原始程式應該是能夠解碼它所儲存文檔中的資訊的唯一實體。這就是備忘錄模式應用於遊戲、文文書處理等程式的軟體設計中的方式,這些程式需要儲存當前內容相關的複雜狀態的快照並在以後恢複。
備忘錄模式:在不破壞封裝的前提下,捕獲一個對象的內部狀態,並在該對象之外儲存這個狀態。這樣以後就可將該對象恢複到原先的儲存狀態。
何時使用備忘錄模式?
當同時滿足以下兩個條件時,應當考慮使用這一模式:
@:需要儲存一個對象(或某部分)在某一個時刻的狀態,這樣以後就可以恢複到先前的狀態。
@:用於擷取狀態的介面會暴漏實現的細節,需要將其隱藏起來。
如何使用備忘錄模式:
在ViewController.m中增加下面的方法:
複製代碼 代碼如下:
- (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.
[[NSUserDefaultsstandardUserDefaults] setInteger:currentAlbumIndex forKey:@"currentAlbumIndex"];
}
- (void)loadPreviousState
{
currentAlbumIndex = [[NSUserDefaultsstandardUserDefaults] integerForKey:@"currentAlbumIndex"];
[self showDataForAlbumAtIndex:currentAlbumIndex];
}
saveCurrentState 儲存當前的專輯索引到NSUserDefaults,NSUserDefaults是IOS提供的儲存應用設定資訊和資料的地方。
loadPreviousState 載入之前儲存的索引。這裡其實不是備忘錄模式完整的實現,但是你已經瞭解到它了。
現在,在ViewController.m的viewDidLoad方法中,在scroller初始化之前增加下面的代碼:
複製代碼 代碼如下:
[self loadPreviousState];
它將在應用啟動的時候載入原先儲存的狀態。但是在什麼時候來儲存應用的狀態呢?你將使用通知來實現它。當應用進入背景時候,IOS會發送UIApplicationDidEnterBackgroundNotification通知,你可以使用這個通知去儲存狀態,這是不是很方便?
在viewDidLoad中增加下面的代碼:
複製代碼 代碼如下:
[[NSNotificationCenterdefaultCenter] addObserver:self selector:@selector(saveCurrentState) name:UIApplicationDidEnterBackgroundNotification object:nil];
現在,當應用進入背景時候,ViewController將通過saveCurrentState方法自動儲存當前的狀態。
現在增加下面的代碼:
複製代碼 代碼如下:
- (void)dealloc
{
[[NSNotificationCenterdefaultCenter] removeObserver:self];
}
這將確保當ViewController被銷毀的時候移除觀察者。
構建和運行你的應用,導航到一個專輯,然後通過Command+Shift+H(模擬器的情況下)將app發送到後台,然後關閉app。再一次開啟app,檢查原先選擇的專輯是不是被顯示在中間:
看起來專輯資料是正確的,但是中間的視圖卻沒有顯示正確的專輯。出了什麼情況?這是可選方法initialViewIndexForHorizontalScroller的目的所在。因為這個方法沒有在委託中實現,這樣的話初始化視圖總是第一個視圖。
為了修正這個問題,在ViewController.m中增加下面的代碼:
複製代碼 代碼如下:
- (NSInteger)initialViewIndexForHorizontalScroller:(HorizontalScroller *)scroller
{
return currentAlbumIndex;
}
現在HorizontalScroller的第一個視圖終於設定為了currentAlbumIndex指定的視圖。這使得app在下次使用的時候還保留了上次使用的狀態。
再一次運行你的app,和之前一樣滾動專輯,停止應用,重啟,確保上面的問題已經修複了:
如果你查看PersistencyManager的init方法,你將注意到專輯資料被寫入程式碼並且每次都要重新建立它們。但是更好的方式是建立專輯列表一次,然後儲存它們到一個檔案,你怎麼儲存專輯資料到一個檔案呢?
一個可選的方式就是迴圈Album的屬性,儲存它們到一個plist檔案中,當它們需要的時候再重新構建它們。這個不是一個最好的方式,因為你需要去編寫與每個類的屬性關聯的特定的代碼。舉例來說如果過會你要建立一個具有不同屬性的Movie類,儲存和載入的代碼需要重新寫。
此外,你也不能儲存每個類的私人變數,因為它們在外面的類中是不可見的。這正是蘋果建立了歸檔(Archiving)機制的原因。
Cocoa Touch架構中的備忘錄模式
Cocoa Touch架構在歸檔、屬性列表序列化和核心資料中採用了備忘錄模式。Cocoa的歸檔是對對象及其屬性還有同其他對象間的關係進行編碼,形成一個文檔,該文檔既可以儲存於檔案系統,也可以在進程或網路間傳送。對象與其他對象的關係被看做對象圖的網路。歸檔過程把對象儲存為一種與架構無關的位元組流,保持對象的標識以及對象之間的關係。對象的類型也同資料一起儲存。從位元組流解碼出來的對象通常用與對象編碼時相同的類型進行執行個體化。
如果想歸檔一個對象,很多時候我們是考慮儲存程式的狀態。在模型-視圖-控制器範式中,程式的狀態通常由模型對象來進行維護。我們把模型對象編碼到文檔,然後再對其解碼讀回來。在運行時使用NSCoder對象進行編碼與解碼操作。NSCoder本身是個抽象類別。蘋果公司建議通過NSCoder的具體類NSKeyArchiver和NSKeyedUnarchiver,使用基於鍵的歸檔技術。被編碼與解碼的對象必須遵守NSCoding協議並實現以下方法:
複製代碼 代碼如下:
- (void)encodeWithCoder:(NSCoder *)aCoder;
- (id)initWithCoder:(NSCoder *)aDecoder;