詳解Objective-C設(shè)計(jì)模式編程中對(duì)備忘錄模式的運(yùn)用
基本理解
這個(gè)模式有三個(gè)關(guān)鍵角色:原發(fā)器(Originator)、備忘錄(Memento)、看管人(caretaker)。三者的基本關(guān)系是:原發(fā)器創(chuàng)建一個(gè)包含其狀態(tài)的備忘錄,并傳給看管人??垂苋瞬恢廊绾闻c備忘錄交互,但會(huì)把備忘錄放在一個(gè)安全之處保管好。
備忘錄(Memento):在 不破壞封裝性的前提下,捕獲一個(gè)對(duì)象的內(nèi)部狀態(tài),并在該對(duì)象之外保存這個(gè)狀態(tài)。這樣以后就可以將該對(duì)象回復(fù)到原先保存的狀態(tài)。
Originator(發(fā)起人):負(fù)責(zé)創(chuàng)建一個(gè)備忘錄,用以記錄當(dāng)前時(shí)刻它的內(nèi)部狀態(tài),并且可使用恢復(fù)備忘錄內(nèi)部狀態(tài)。Originator可根據(jù)需要決定Memento存儲(chǔ)Originator的哪些內(nèi)部狀態(tài)。
Memento(備忘錄):負(fù)責(zé)存儲(chǔ)Originator對(duì)象的內(nèi)部狀態(tài),并可防止Originator以外的其他對(duì)象訪問備忘錄。備忘錄有兩個(gè)接口,CareTaker
只能看到備忘錄的窄接口,它只能將備忘錄傳給其他對(duì)象。originator能夠看到一個(gè)寬接口,允許它訪問返回到先前狀態(tài)所需的所有數(shù)據(jù)。
Caretaker(管理者):負(fù)責(zé)保存好備忘錄,不能對(duì)備忘錄的內(nèi)容進(jìn)行操作或檢查。
就是把要保存的細(xì)節(jié)給封裝在了Memento中,哪一天要更改保存的細(xì)節(jié)也不用影響客戶端了。
備忘錄使用場合
備忘錄模式比較適用于功能比較復(fù)雜的,但需要維護(hù)或記錄屬性歷史的類,或者需要保存的屬性只是眾多屬性中的一小部分時(shí),Orignator可以根據(jù)保存的Memento信息還原到前一狀態(tài)。
如果在某個(gè)系統(tǒng)中使用命令模式時(shí),需要實(shí)現(xiàn)命令的撤銷功能,那么命令模式可以使用備忘錄模式來存儲(chǔ)撤銷操作的狀態(tài)。有的時(shí)候一些對(duì)象的內(nèi)部信息必須要保存在對(duì)象以外的地方,但是必須要由對(duì)象自己讀取,這時(shí),使用備忘錄可以把復(fù)雜的對(duì)象內(nèi)部信息對(duì)其他的對(duì)象屏蔽起來。
用于獲取狀態(tài)的接口會(huì)暴露實(shí)現(xiàn)接口,需要將其屏蔽起來。
它一般應(yīng)用于游戲、文字處理程序的設(shè)計(jì)中,這種程序需要保存當(dāng)前上下文的復(fù)雜狀態(tài)的快照并在以后恢復(fù)處理。
作用
當(dāng)角色的狀態(tài)改變時(shí),有可能這個(gè)狀態(tài)無效,這時(shí)候就可以使用暫時(shí)存儲(chǔ)起來的備忘錄將狀態(tài)復(fù)原。
Cocoa Touch框架中的備忘錄模式
Cocoa Touch框架在歸檔、屬性列表序列化和核心數(shù)據(jù)采用了備忘錄模式。
Cocoa的歸檔是對(duì)對(duì)象以及其屬性還有同其他對(duì)象間的關(guān)系進(jìn)行編碼,形成一個(gè)文檔,該文檔既可保存與文件系統(tǒng)中,也可在進(jìn)程或網(wǎng)絡(luò)間傳送。對(duì)象與其他對(duì)象的關(guān)系被看做對(duì)象圖的網(wǎng)絡(luò)。
歸檔過程把對(duì)象保存為一種與架構(gòu)無關(guān)的字節(jié)流,保持對(duì)象的標(biāo)識(shí)以及對(duì)象之間的關(guān)系。對(duì)象的類型也同數(shù)據(jù)一起保存。從字節(jié)流解碼出來的對(duì)象通常用于對(duì)象編碼時(shí)相同的類進(jìn)行實(shí)例化。使用NSCoder的具體類NSKeyedArchiver和NSKeyedUnarchiver,使用基于鍵的歸檔技術(shù),被編碼與解碼的對(duì)象必須遵守NSCoding協(xié)議并實(shí)現(xiàn)以下方法:
-(id)initWithCoder:(NSCoder *)coder;
-(void)encodeWithCoder:(NSCoder *)coder;
實(shí)例
添加下面兩個(gè)方法到 ViewController.m 文件:
- (void)saveCurrentState
{
// 當(dāng)用戶退出應(yīng)用之后再重新打開,他想要跟他之前退出時(shí)一樣的狀態(tài)
// 退出應(yīng)用,這個(gè)時(shí)候我們需要做的是把當(dāng)前顯示的專輯存儲(chǔ)下來
// 因?yàn)橹挥幸恍∑畔ⅲ覀兛捎?NSUserDefaults 來存儲(chǔ)信息
[[NSUserDefaults standardUserDefaults] setInteger:currentAlbumIndex forKey:@“currentAlbumIndex”];
}
- (void)loadPreviousState
{
currentAlbumIndex = [[NSUserDefaults standardUserDefaults] integerForKey@“currentAlbumIndex”];
[self showDataForAlbumAtIndex:currentAlbumIndex];
}
saveCurrentState 存儲(chǔ)當(dāng)前專輯的索引到 NSUserDefaults ─ NSUserDefaults 是一個(gè)標(biāo)準(zhǔn)數(shù)據(jù)存儲(chǔ),iOS 用來專門存放程序設(shè)置和數(shù)據(jù)。
loadPreviousState 加載這之前存儲(chǔ)的專輯索引。這不是備忘錄模式的全部,不過你已經(jīng)達(dá)到目的了。
現(xiàn)在,在 ViewController.m 里,滾動(dòng)視圖初始化之前,在 viewDidLoad 里添加下面一行:
[self loadPreviousState];
當(dāng)程序啟動(dòng)的時(shí)候加載上一次存儲(chǔ)的狀態(tài)。但是你在哪里存儲(chǔ)程序的當(dāng)前狀態(tài)呢?你需要使用通知來做這樣的事情。當(dāng)程序進(jìn)入后臺(tái)時(shí),iOS 會(huì)發(fā)送一個(gè) UIApplicationDidEnterBackgroundNotification 通知。你可利用這個(gè)通知調(diào)用 saveCurrentState。就這么方便?
在 viewDidLoad: 最后面添加下面一行
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(saveCurrentState) name:UIApplicationDidEnterBackgroundNotification object:nil];
現(xiàn)在,當(dāng)你的 app 進(jìn)入后臺(tái)運(yùn)行后,ViewController 會(huì)自動(dòng)調(diào)用 saveCurrentState 存儲(chǔ)當(dāng)前的狀態(tài)。
現(xiàn)在,添加下面代碼:
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
這里是確保當(dāng) ViewController 被釋放時(shí),移除類的 Observer。
構(gòu)建和運(yùn)行你的 app,點(diǎn)擊到一個(gè)專輯,用 Command+Shift+H(如果你使用的是模擬器的話) 將程序在后臺(tái)運(yùn)行,然后關(guān)掉 app。重啟 app,檢查之前選擇的專輯是不是居中顯示:
專輯數(shù)據(jù)看起來是對(duì)的,但是正確的專輯封面確沒有居中,哪出問題了?
這就是可選方法 initialViewIndexForHorizontalScroller 的用處!因?yàn)檫@個(gè)方法沒有被委托執(zhí)行,ViewController 在這種情況下總是會(huì)顯示默認(rèn)的第一個(gè)專輯封面。
修復(fù)這個(gè)問題,在 ViewController.m 中添加如下代碼:
- (NSInteger)initialViewIndexForHorizontalScroller:(HorizontalScroller *)scroller
{
return currentAlbumIndex;
}
現(xiàn)在 HorizontalScroller 的第一個(gè)視圖總是會(huì)被設(shè)置成 currentAlbumIndex 索引的圖片。這種方法能夠確保你的 app 有一個(gè)非常棒的用戶體驗(yàn),并且它是可恢復(fù)的。
重新運(yùn)行你的 app,滾動(dòng)專輯封面,關(guān)閉 app,然后重啟確保問題已經(jīng)得到解決:
如果你查看 PersistencyManger 的初始化方法,你會(huì)注意到專輯的數(shù)據(jù)是一種硬編碼,PersistencyManger 每次創(chuàng)建,數(shù)據(jù)也會(huì)重復(fù)創(chuàng)建一次。有沒有一種更好的方法當(dāng)專輯列表被創(chuàng)建的時(shí)候就存儲(chǔ)它們呢。那么如何把專輯數(shù)據(jù)存儲(chǔ)到文件里呢?
一種選擇就是循環(huán)訪問 Album 的屬性,然后把它存儲(chǔ)在一個(gè) plist 文件里,當(dāng)需要它們的時(shí)候重新創(chuàng)建一個(gè) Album 的實(shí)例。這不是最好的選擇,這需要你在每一個(gè)類里根據(jù)不同的數(shù)據(jù)或?qū)傩詫懱囟ǖ拇a。例子,如果稍后你需要一個(gè)電影的類,里面有一些不同的屬性,存儲(chǔ)和加載這些數(shù)據(jù)你就需要寫一些新的代碼。
此外,你不能在每一個(gè)類的實(shí)例里存儲(chǔ)私有變量,因?yàn)樗麄兪遣豢稍L問的外部類。這就是為什么蘋果要?jiǎng)?chuàng)建歸檔 (Archiving) 機(jī)制。
- Objective-C編程中語句和變量的一些編寫規(guī)范建議
- 舉例講解Objective-C中@property屬性的用法
- 詳解Objective-C編程中對(duì)設(shè)計(jì)模式中適的配器模式的使用
- 淺析Objective-C中分類Category的使用
- Objective-C的NSOperation多線程類基本使用指南
- 簡介Objective-C解析XML與JSON數(shù)據(jù)格式的方法
- 簡單講解Objective-C的基本特性及其內(nèi)存管理方式
- 理解Objective-C的變量以及面相對(duì)象的繼承特性
- 以實(shí)例講解Objective-C中的KVO與KVC機(jī)制
- Objective-C中編程中一些推薦的書寫規(guī)范小結(jié)
相關(guān)文章
iOS實(shí)現(xiàn)多個(gè)垂直滑動(dòng)條并列視圖
這篇文章主要為大家詳細(xì)介紹了iOS實(shí)現(xiàn)多個(gè)垂直滑動(dòng)條并列視圖,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03Flutter CustomPaint繪制widget使用示例
這篇文章主要為大家介紹了Flutter CustomPaint繪制widget使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12iOS安全防護(hù)系列之ptrace反調(diào)試與匯編調(diào)用系統(tǒng)方法詳解
這篇文章主要給大家介紹了關(guān)于iOS安全防護(hù)系列之ptrace反調(diào)試與匯編調(diào)用系統(tǒng)方法的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-07-07詳解iOS 實(shí)現(xiàn)一對(duì)多代理方案
本文主要介紹了iOS 實(shí)現(xiàn)一對(duì)多代理方案,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-012016 cocoapods的安裝和使用方法以及版本升級(jí)遇到的問題
CocoaPods是一個(gè)負(fù)責(zé)管理iOS項(xiàng)目中第三方開源庫的工具,通過CocoaPods,我們可以將第三方的依賴庫統(tǒng)一管理起來,配置和更新只需要通過簡單的幾行命令即可完成,需要的朋友可以參考下2016-09-09