何為備忘錄模式?
在響應某些事件時,應用程序需要保存自身的狀態,比如當用戶保存文檔或程序退出時。例如,游戲退出之前,可能需要保存當前會話的狀態,如游戲等級、敵人數量、可用武器的種類等。游戲再次打開時,玩家可以從離開的地方接著玩。很多時候,保存程序的狀態真的不需要什么特別巧妙的方法。任何簡單有效的方法都可以,但是同時,保存信息應該只對原始程序有意義。原始程序應該是能夠解碼它所保存文檔中的信息的唯一實體。這就是備忘錄模式應用于游戲、文字處理等程序的軟件設計中的方式,這些程序需要保存當前上下文的復雜狀態的快照并在以后恢復。
備忘錄模式:在不破壞封裝的前提下,捕獲一個對象的內部狀態,并在該對象之外保存這個狀態。這樣以后就可將該對象恢復到原先的保存狀態。
何時使用備忘錄模式?
當同時滿足以下兩個條件時,應當考慮使用這一模式:
@:需要保存一個對象(或某部分)在某一個時刻的狀態,這樣以后就可以恢復到先前的狀態。
@:用于獲取狀態的接口會暴漏實現的細節,需要將其隱藏起來。
如何使用備忘錄模式:
在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;