保存和恢復(fù)過程
狀態(tài)保存和恢復(fù)是可選的功能,并需要在應(yīng)用的幫助下工作。應(yīng)用辨認(rèn)出那些應(yīng)該被存儲(chǔ)的對(duì)象伺糠,UIKit會(huì)在恰當(dāng)?shù)臅r(shí)候執(zhí)行保存和恢復(fù)這些對(duì)象的工作。因?yàn)閁IKit處理如此多的過程酱讶,所以理解它在幕后的工作對(duì)于你知道自定義代碼是如何適應(yīng)整體方案有幫助退盯。
當(dāng)思考狀態(tài)保存和恢復(fù)的時(shí)候,分成兩部分進(jìn)行是有幫助的泻肯。UIKit在恰當(dāng)?shù)臅r(shí)間保存應(yīng)用狀態(tài)渊迁,例如當(dāng)應(yīng)用從前臺(tái)進(jìn)入后臺(tái)。當(dāng)UIKit確定新狀態(tài)信息是必須的時(shí)候灶挟,它查看應(yīng)用的視圖和視圖控制器琉朽,以便知道哪些應(yīng)該被保存。對(duì)于每個(gè)這樣的對(duì)象稚铣,UIKit把存儲(chǔ)相關(guān)的數(shù)據(jù)寫入加密的磁盤文件中箱叁。下一次墅垮,當(dāng)應(yīng)用重新啟動(dòng)的時(shí)候,UIKit尋找這些文件耕漱,如果存在算色,使用它們來(lái)恢復(fù)應(yīng)用的狀態(tài)。因?yàn)檫@些文件是加密的螟够,所以狀態(tài)的保存和恢復(fù)必須是在設(shè)備解鎖的狀態(tài)下進(jìn)行灾梦。
在恢復(fù)狀態(tài)期間,UIKit使用保存的數(shù)據(jù)來(lái)重新構(gòu)建界面妓笙,但實(shí)際對(duì)象的創(chuàng)建由你的代碼完成若河。因?yàn)閼?yīng)用或許從storyboard文件自動(dòng)加載對(duì)象,只有你的代碼知道哪個(gè)對(duì)象需要被創(chuàng)建寞宫,而哪個(gè)對(duì)象已經(jīng)存在可以簡(jiǎn)單的返回萧福。在創(chuàng)建了每個(gè)對(duì)象之后,UIKit使用存儲(chǔ)的狀態(tài)信息初始化它們辈赋。
在存儲(chǔ)和恢復(fù)狀態(tài)期間鲫忍,應(yīng)用有一些責(zé)任。
- 在存儲(chǔ)期間炭庙,應(yīng)用的責(zé)任有:
- 包塑UIKit它支持狀態(tài)存儲(chǔ)饲窿。
- 告訴UIKit哪個(gè)視圖控制器和視圖應(yīng)該被保存。
- 為任何被保存的對(duì)象編碼相關(guān)數(shù)據(jù)焕蹄。
- 在恢復(fù)期間逾雄,應(yīng)用的責(zé)任:
- 告訴UIKit它支持狀態(tài)恢復(fù)。
- 提供(或創(chuàng)建)那些被UIKit要求的對(duì)象腻脏。
- 返回對(duì)象到它之前的狀態(tài)鸦泳。
在應(yīng)用的責(zé)任方面,最重要的就是告訴UIKit哪個(gè)對(duì)象需要保存永品,并在以后應(yīng)用啟動(dòng)期間提供者這些對(duì)象做鹰。這兩個(gè)行為放置在何處,是你在設(shè)計(jì)應(yīng)用保存和恢復(fù)代碼的時(shí)候需要花大量時(shí)間思考的鼎姐。它們也是你對(duì)實(shí)際過程最有力的控制钾麸。為了理解為什么是這樣,看一個(gè)例子會(huì)有所幫助炕桨。
圖5-1 展示了有多個(gè)選項(xiàng)卡的選項(xiàng)卡欄界面視圖控制器的層級(jí)結(jié)構(gòu)饭尝。正如你看到的,一些視圖控制器作為應(yīng)用主storyboard文件被自動(dòng)加載献宫,但是另一些視圖控制器卻沒有钥平。在沒有狀態(tài)恢復(fù)功能的情況下,只有從主storyboard文件加載的視圖控制器會(huì)在重新啟動(dòng)時(shí)恢復(fù)狀態(tài)姊途。但通過添加支持狀態(tài)恢復(fù)功能到你的應(yīng)用涉瘾,你可以保留所有視圖控制器
圖5-1 視圖控制器層級(jí)結(jié)構(gòu)樣本
UIKit只保存那些分配有恢復(fù)標(biāo)識(shí)符的對(duì)象知态。恢復(fù)標(biāo)識(shí)符是一個(gè)字符串立叛,它用于標(biāo)識(shí)UIKit和應(yīng)用的視圖和視圖控制器负敏。這個(gè)字符串的值僅對(duì)你的代碼有意義,但是此字符串的存在告訴UIKit它需要保存標(biāo)記的對(duì)象囚巴。在保存過程期間原在,UIKit會(huì)巡視應(yīng)用的控制器層級(jí)并保存所有具有恢復(fù)標(biāo)識(shí)符的對(duì)象。如果視圖控制器沒有恢復(fù)標(biāo)識(shí)符彤叉,這些視圖控制器以及它們的自視圖和子視圖控制器不會(huì)被保存。圖5-2展示了之前視圖層級(jí)結(jié)構(gòu)的更新版本村怪,現(xiàn)在大多(不是全部)數(shù)視圖控制器都有了恢復(fù)標(biāo)識(shí)符秽浇。
圖5-2 給視圖控制器添加恢復(fù)標(biāo)識(shí)符
根據(jù)應(yīng)用的需要,保存每個(gè)視圖控制器有可能沒有意義甚负。如果視圖控制器出現(xiàn)一個(gè)臨時(shí)的信息柬焕,你或許不想在返回的時(shí)候還看到它,而是希望給用戶一個(gè)更穩(wěn)定的界面梭域。
如果選擇保存所有視圖控制器的狀態(tài)斑举,你也需要決定你如何在之后恢復(fù)它們。UIKit提供兩種創(chuàng)建對(duì)象的方式病涨。你可以讓應(yīng)用的委托創(chuàng)建它們富玷,或者可以為視圖控制器分配一個(gè)恢復(fù)類并讓這個(gè)類來(lái)創(chuàng)建它們。一個(gè)恢復(fù)類要實(shí)現(xiàn)UIViewControllerRestoration協(xié)議既穆,并且在恢復(fù)時(shí)負(fù)責(zé)查找和創(chuàng)建指定的對(duì)象赎懦。下面有這對(duì)每種情況的一些提示:
- 如果視圖控制器在啟動(dòng)時(shí)始終從主storyboard文件加載,不要分配恢復(fù)類幻工。取而代之的是励两,讓應(yīng)用的委托來(lái)查找這個(gè)對(duì)象,或者充分利用UIKit的支持來(lái)隱式查找恢復(fù)的對(duì)象囊颅。
- 對(duì)于啟動(dòng)時(shí)不從主storyboard文件加載的視圖控制器当悔,分配一個(gè)恢復(fù)類。最簡(jiǎn)單的方式是讓每個(gè)視圖控制器成為自身的恢復(fù)類踢代。
在存儲(chǔ)過程期間盲憎,UIKit識(shí)別要保存的對(duì)象,并把每個(gè)受影響對(duì)象的狀態(tài)寫入磁盤奸鬓。每個(gè)視圖控制器對(duì)象都有一個(gè)機(jī)會(huì)來(lái)寫入它們想保存的任何數(shù)據(jù)焙畔。例如,一個(gè)選項(xiàng)卡視圖控制器保存被選中的選項(xiàng)卡的標(biāo)識(shí)符串远。UIKit也保存像視圖控制器的恢復(fù)類這樣的信息到磁盤宏多。如果每個(gè)視圖控制器的視圖都有恢復(fù)標(biāo)識(shí)符儿惫,UIKit也會(huì)要求它們保存狀態(tài)信息。
在下次應(yīng)用啟動(dòng)的時(shí)候伸但,UIKit如常的加載應(yīng)用的主storyboard或nib文件肾请,調(diào)用應(yīng)用委托的application:willFinishLaunchingWithOptions:方法,然后嘗試恢復(fù)應(yīng)用之前的狀態(tài)更胖。首先铛铁,它會(huì)要求應(yīng)用程序提供與存儲(chǔ)的匹配的一系列視圖控制器對(duì)象。如果給丁的視圖控制器有分配恢復(fù)類却妨,這個(gè)恢復(fù)類會(huì)被要求提供對(duì)象饵逐;否則,應(yīng)用委托將被要求提供對(duì)象彪标。
保存過程的流程
圖5-3 展示了在狀態(tài)保存期間發(fā)生的高級(jí)事件倍权,還顯示了應(yīng)用的對(duì)象如何被影響。在保存事件發(fā)生之前捞烟,UIKit會(huì)要求應(yīng)用委托調(diào)用application:shouldSaveApplicationState:方法薄声。如果這個(gè)方法返回YES,UIKit開始收集并編碼應(yīng)用的視圖和視圖控制器题画。當(dāng)此過程完成時(shí)默辨,它把編碼的數(shù)據(jù)寫入磁盤。
圖5-3 界面保存高層級(jí)工作流
在下一次應(yīng)用啟動(dòng)的時(shí)候苍息,系統(tǒng)自動(dòng)查找存儲(chǔ)狀態(tài)的文件缩幸,如果存在,使用它們來(lái)恢復(fù)界面档叔。因?yàn)榇藸顟B(tài)信息只和應(yīng)用的上次及本次啟動(dòng)周期有關(guān)桌粉,所以該文件通常在應(yīng)用啟動(dòng)完成后被丟棄。這個(gè)文件也會(huì)因恢復(fù)錯(cuò)誤而丟棄衙四。例如铃肯,如果應(yīng)用在恢復(fù)的過程中崩潰,系統(tǒng)就會(huì)在下次啟動(dòng)周期期間自動(dòng)的丟棄狀態(tài)信息來(lái)防止再一次崩潰传蹈。
恢復(fù)過程的流程
圖5-4展示了在狀態(tài)恢復(fù)期間發(fā)生的高級(jí)事件押逼,以及展示了應(yīng)用的對(duì)象如何被影響。在標(biāo)準(zhǔn)的初始化和UI加載完成后惦界,UIKit會(huì)通過調(diào)用application:shouldRestoreApplicationState:方法來(lái)詢問應(yīng)用程序委托挑格,狀態(tài)恢復(fù)是否可以進(jìn)行。這是應(yīng)用的委托檢查保存的數(shù)據(jù)以及決定是否有可能進(jìn)行狀態(tài)恢復(fù)的機(jī)會(huì)沾歪。如果是漂彤,UIKit使用應(yīng)用委托和狀態(tài)恢復(fù)類來(lái)獲取應(yīng)用視圖控制器的引用。然后每個(gè)對(duì)象就使用它需要的數(shù)據(jù)恢復(fù)到之前的狀態(tài)。
圖5-4 恢復(fù)應(yīng)用用戶界面的高級(jí)流程
雖然UIKit幫助恢復(fù)各個(gè)視圖控制器挫望,但它不能自動(dòng)恢復(fù)這些視圖控制器之間的關(guān)系立润。每個(gè)視圖控制器要負(fù)責(zé)編碼足夠的狀態(tài)信息以便能使自己恢復(fù)到之前的狀態(tài)。例如媳板,一個(gè)導(dǎo)航控制器編碼在導(dǎo)航棧中的視圖控制器的順序桑腮。然后在以后使用這些信息來(lái)返回到之前的狀態(tài)。其他嵌套子視圖控制器的視圖控制器也有類似的編碼責(zé)任蛉幸。
注意:不是所有的視圖控制器都需要編碼它們的子視圖控制器破讨。例如,選項(xiàng)欄視圖控制器奕纫。它假設(shè)應(yīng)用遵循優(yōu)先創(chuàng)建合適的自視圖控制器提陶,然后再創(chuàng)建標(biāo)簽欄控制器的常規(guī)模式。
因?yàn)槟阖?fù)責(zé)重新創(chuàng)建應(yīng)用的視圖控制器若锁,所以你再恢復(fù)過程期間有一些靈活的方式來(lái)改變應(yīng)用的界面搁骑。例如,你可以重新對(duì)選項(xiàng)欄控制器的選項(xiàng)卡進(jìn)行重排序又固,而且仍然可以使用存儲(chǔ)的數(shù)據(jù)來(lái)恢復(fù)到之前的狀態(tài)。當(dāng)然煤率,如果你對(duì)視圖控制器層級(jí)進(jìn)行了顯著的更改仰冠,例如在應(yīng)用更新期間,你可能無(wú)法使用存儲(chǔ)的數(shù)據(jù)蝶糯。
當(dāng)你排除視圖控制器群組時(shí)會(huì)發(fā)生什么洋只?
當(dāng)視圖控制器的恢復(fù)標(biāo)識(shí)符為nil時(shí),視圖控制器和它的自視圖控制器不會(huì)自動(dòng)保存昼捍。例如识虚,在圖5-5中,因?yàn)閷?dǎo)航控制器沒有恢復(fù)標(biāo)識(shí)符妒茬,它和它的所有子視圖控制器及視圖會(huì)從保存的數(shù)據(jù)中被刪除担锤。
圖5-5 從自動(dòng)存儲(chǔ)過程中排除視圖控制器
即使你決定不保存視圖控制器,也不意味著這些視圖控制器會(huì)完全從視圖層級(jí)中消失乍钻。在啟動(dòng)的時(shí)候肛循,應(yīng)用仍然會(huì)以默認(rèn)設(shè)置的方式創(chuàng)建這些視圖控制器。例如银择,如果任何從應(yīng)用的storyboard文件自動(dòng)加載的視圖控制器多糠,它們讓然會(huì)出現(xiàn),盡管它們時(shí)默認(rèn)的配置浩考。如圖5-6所示夹孔。
圖5-6 加載默認(rèn)的一組視圖控制器
還有一點(diǎn)需要注意,即使視圖控制器沒有自動(dòng)保存,你仍然可以對(duì)該視圖控制器的引用進(jìn)行編碼并進(jìn)行手動(dòng)存儲(chǔ)搭伤。在圖5-5中只怎,第一個(gè)導(dǎo)航控制器有三個(gè)子視圖控制器有恢復(fù)標(biāo)識(shí)符,即使父視圖控制器沒有闷畸。如果應(yīng)用的委托(或者任何保存對(duì)象)編碼這些視圖控制器的引用尝盼,它們的狀態(tài)也會(huì)被保存。即使它們?cè)趯?dǎo)航控制器的順序沒有被保存佑菩,你仍能夠使用這些引用來(lái)創(chuàng)建這些視圖控制器盾沫,并在后續(xù)的啟動(dòng)周期期間安裝它們到導(dǎo)航控制器。
實(shí)現(xiàn)狀態(tài)保存和恢復(fù)的檢查清單
支持狀態(tài)保存和恢復(fù)要求修改應(yīng)用委托和視圖控制器對(duì)象來(lái)編碼和解碼狀態(tài)信息殿漠。如果應(yīng)用有任何自定義的視圖赴精,就也需要可保存狀態(tài)的信息。你需要也需要修改這些對(duì)象绞幌。
當(dāng)在代碼中添加狀態(tài)保存和恢復(fù)內(nèi)容時(shí)蕾哟,使用以下列表來(lái)提醒你需要編寫的代碼。
- (要求)實(shí)現(xiàn)應(yīng)用委托的application:shouldSaveApplicationState: 和 application:shouldRestoreApplicationState:方法莲蜘。參見 Enabling State Preservation and Restoration in Your App谭确。
- (要求)給每個(gè)你想保存的視圖控制器分配恢復(fù)標(biāo)識(shí)符,該標(biāo)識(shí)符不能為空字符串票渠,分配的位置是視圖控制器的restorationIdentifier屬性逐哈;參見Marking Your View Controllers for Preservation。如果你也想保存特定視圖的狀態(tài)问顷,分配非空字符串給它們的restorationIdentifier屬性昂秃;參見Preserving the State of Your Views。
- (要求)從應(yīng)用委托的application:willFinishLaunchingWithOptions:方法展示應(yīng)用的視窗杜窄。狀態(tài)恢復(fù)機(jī)制需要視窗肠骆,以便應(yīng)用能夠恢復(fù)滾動(dòng)位置以及其他相關(guān)的應(yīng)用界面。
- 分配恢復(fù)類給合適的視圖控制器塞耕。(如果你沒有這樣做蚀腿,應(yīng)用的委托會(huì)在恢復(fù)的時(shí)候被要起提供相關(guān)的視圖控制器。)參見Restoring Your View Controllers at Launch Time荷科。
- (推薦)使用視圖和視圖控制器的encodeRestorableStateWithCoder: 和 decodeRestorableStateWithCoder:來(lái)編解碼它們的狀態(tài)唯咬。參見Encoding and Decoding Your View Controller’s State。
- 使用應(yīng)用委托的application:willEncodeRestorableStateWithCoder: 和 application:didDecodeRestorableStateWithCoder:方法來(lái)為應(yīng)用編解碼版本信息和額外的狀態(tài)信息畏浆。參見 Preserving Your App’s High-Level State胆胰。
- 作為表視圖以及集合視圖的數(shù)據(jù)源的對(duì)象,應(yīng)該實(shí)現(xiàn)UIDataSourceModelAssociation協(xié)議刻获。雖然不是必須的蜀涨,但是這個(gè)協(xié)議可以幫助保存和選擇在這些類型的視圖中的可見項(xiàng)目。參見Implementing Preservation-Friendly Data Sources。
在應(yīng)用中啟用狀態(tài)保存和恢復(fù)
狀態(tài)保存和恢復(fù)不是自動(dòng)功能厚柳,應(yīng)用必須選擇使用它氧枣。應(yīng)用通過在它們應(yīng)用代理中實(shí)現(xiàn)下面的兩個(gè)方法來(lái)支持這個(gè)功能:
application:shouldSaveApplicationState:
application:shouldRestoreApplicationState:
通常,你只要讓這些方法返回YES就表明這個(gè)功能能夠發(fā)生别垮。但是便监,如果不想讓此功能發(fā)生,可以讓方法返回NO碳想。例如烧董,在釋放和更新應(yīng)用之后,如果應(yīng)用不能有效的從之前的版本回復(fù)狀態(tài)胧奔,你可以讓application:shouldRestoreApplicationState:方法返回NO逊移。
存儲(chǔ)視圖控制器的狀態(tài)
存儲(chǔ)應(yīng)用的視圖控制器的狀態(tài)應(yīng)該是你的主要目標(biāo)。視圖控制器定義了用戶界面的結(jié)構(gòu)龙填。它們管理必要的視圖來(lái)顯示界面胳泉,并協(xié)調(diào)處理從這些視圖返回的數(shù)據(jù)。為了存儲(chǔ)單視圖控制器的狀態(tài)岩遗,你必須做到以下幾點(diǎn):
- (要求)分配恢復(fù)標(biāo)識(shí)符給視圖控制器扇商,參見Marking Your View Controllers for Preservation。
- (要求)提供在應(yīng)用啟動(dòng)的時(shí)候創(chuàng)建或定位新視圖控制器的對(duì)象宿礁;參見Restoring Your View Controllers at Launch Time钳吟。
- (可選)實(shí)現(xiàn)encodeRestorableStateWithCoder: 和 decodeRestorableStateWithCoder:方法來(lái)編碼和恢復(fù)任何狀態(tài)信息,這些信息在下一次啟動(dòng)期間不會(huì)被重新創(chuàng)建窘拯;參見Encoding and Decoding Your View Controller’s State。
(未完待續(xù)......)