iOS 文件和数据管理 (可能会删除本地文件储存)
轉自:http://www.apple.com.cn/developer/iphone/library/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/FilesandNetworking/FilesandNetworking.html
文件和數據管理
iPhone OS系統上的文件和用戶的媒體數據及個人文件共享閃存上的空間。出于安全的目的,您的應用程序被放在其自己的目錄下,并且只能對該目錄進行讀寫。本章的下面部分將描述應用程序本地文件系統的結構及幾個讀寫文件的技術。
常用目錄
出于安全的目的,應用程序只能將自己的數據和偏好設置寫入到幾個特定的位置上。當應用程序被安裝到設備上時,系統會為其創建一個家目錄。表6-1列出了應用程序家目錄下的一些重要子目錄,您的程序可能需要對其進行訪問。表中還描述了每個目錄的設計目的和訪問限制,以及iTunes是否對該目錄下的內容進行備份。有關備份和恢復過程的更多信息,請參見“備份和恢復”?部分;有關應用程序家目錄本身的信息,則請參見?“應用程序沙箱”部分。
| <Application_Home>/AppName.app | 這是程序包目錄,包含應用程序的本身。由于應用程序必須經過簽名,所以您在運行時不能對這個目錄中的內容進行修改,否則可能會使應用程序無法啟動。 在iPhone OS 2.1及更高版本的系統,iTunes不對這個目錄的內容進行備份。但是,iTunes會對在App Store上購買的應用程序進行一次初始的同步。 |
| <Application_Home>/Documents/ | 您應該將所有的應用程序數據文件寫入到這個目錄下。這個目錄用于存儲用戶數據或其它應該定期備份的信息。有關如何取得這個目錄路徑的信息,請參見“獲取應用程序目錄的路徑”部分。 iTunes會備份這個目錄的內容。 |
| <Application_Home>/Library/Preferences | 這個目錄包含應用程序的偏好設置文件。您不應該直接創建偏好設置文件,而是應該使用NSUserDefaults類或CFPreferences?API來取得和設置應用程序的偏好,詳情請參見“添加Settings程序包”部分。 iTunes會備份這個目錄的內容。 |
| <Application_Home>/Library/Caches | 這個目錄用于存放應用程序專用的支持文件,保存應用程序再次啟動過程中需要的信息。您的應用程序通常需要負責添加和刪除這些文件,但在對設備進行完全恢復的過程中,iTunes會刪除這些文件,因此,您應該能夠在必要時重新創建。您可以使用“獲取應用程序目錄的路徑”?部分描述的接口來獲取該目錄的路徑,并對其進行訪問。 在iPhone OS 2.2及更高版本,iTunes不對這個目錄的內容進行備份。 |
| <Application_Home>/tmp/ | 這個目錄用于存放臨時文件,保存應用程序再次啟動過程中不需要的信息。當您的應用程序不再需要這些臨時文件時,應該將其從這個目錄中刪除(系統也可能在應用程序不運行的時候清理留在這個目錄下的文件)。有關如何獲得這個目錄路徑的信息,請參見“獲取應用程序目錄的路徑”部分。 在iPhone OS 2.1及更高版本,iTunes不對這個目錄的內容進行備份。 |
備份和恢復
您不需要在應用程序中為備份和恢復操作做任何準備。在iPhone OS 2.2及更高版本的系統中,當設備被連接到計算機并完成同步時,iTunes會對除了下面這些目錄之外的所有文件進行增量式的備份:
-
<Application_Home>/AppName.app
-
<Application_Home>/Library/Caches
-
<Application_Home>/tmp
雖然iTunes確實對應用程序的程序包本身進行備份,但并不是在每次同步時都進行這樣的操作。通過設備上的App Store購買的應用程序在下一次設備和iTunes同步時進行備份。而在之后的同步操作中,應用程序并不進行備份,除非應用程序包本身發生了變化(比如由于應用程序被更新了)。
為了避免同步過程花費太長時間,您應該有選擇地往應用程序家目錄中存放文件。<Application_Home>/Documents目錄應該用于存放用戶數據文件或不容易在應用程序中重新創建的文件。存儲臨時數據的文件應該放在Application Home/tmp目錄,而且應該在不需要的時候將其刪除。如果您的應用程序需要創建用于下次啟動的數據文件,則應該將那些文件放到Application Home/Library/Caches目錄下。
請注意:如果您的應用程序需要創建數據量大或頻繁變化的文件,則應該考慮將它們存儲在Application Home/Library/Caches目錄下,而不是<Application_Home>/Documents目錄。備份大數據文件會使備份過程顯著變慢,備份頻繁變化(因此必須頻繁備份)的文件也同樣如此。將這些文件放到Caches目錄下可以避免每次同步都對其進行備份(在iPhone OS 2.2及更高版本)。
有關如何在應用程序中使用目錄的更多信息,請參見表6-1。
在應用程序更新過程中被保存的文件
更新應用程序就是將用戶下載的新版應用程序代替之前的版本。在這個過程中,iTunes會將更新過的應用程序安裝到新的應用程序目錄下,并在刪除老版本之前,將用戶數據文件轉移到新的應用程序目錄下。在更新的過程中,iTunes保證如下目錄中的文件會得以保留:
-
<Application_Home>/Documents
-
<Application_Home>/Library/Preferences
雖然其它用戶目錄下的文件也可能被轉移,但是您不應該假定更新之后該文件還仍然存在。
Keychain數據
keychain是一個安全、經過加密保護的容器,用于保存密碼和其它秘密信息。應用程序的keychain數據存儲在應用程序沙箱之外。如果應用程序被卸載,則該數據會自動被刪除。當用戶通過iTunes備份應用程序數據時,keychain數據也會被備份。然而,keychain數據只能被恢復到之前做備份的設備上。應用程序的更新并不影響其keychain數據。
有關iPhone OS keychain的更多信息,請參見Keychain服務編程指南文檔中的“Keychain服務的概念”部分。
獲取應用程序目錄的路徑
系統在各個級別上都提供了用于獲取應用程序沙箱目錄路徑的編程方法。然而,取得這些路徑的推薦方式還是使用Cocoa編程接口。NSHomeDirectory函數(在Foundation框架中)負責返回頂級家目錄的路徑—也就是包含應用程序、Documents、Library、和tmp目錄的路徑。除了這個函數,您還可以用NSSearchPathForDirectoriesInDomains和NSTemporaryDirectory函數來取得Documents、Caches、和tmp目錄的準確路徑。
NSHomeDirectory和NSTemporaryDirectory函數都通過NSString對象返回正確格式的路徑。您可以通過NSString類提供的與路徑相關的方法來修改路徑信息或創建新的路徑字符串。舉例來說,在取得臨時的目錄路徑之后,您可以附加一個文件名,并用結果字符串在臨時目錄下創建給定名稱的文件。
請注意:如果您使用帶有ANSI C編程接口的框架—包括那些接受路徑參數的接口—請記住NSString對象和其在Core Foundation框架中的等價類型之間是“免費橋接”的。這意味著您可以將一個NSString對象(比如上述某個函數的返回結果)強制類型轉換為一個CFStringRef類型,如下面的例子所示:
| CFStringRef homeDir = (CFStringRef)NSHomeDirectory(); |
Foundation框架中的NSSearchPathForDirectoriesInDomains函數用于取得幾個應用程序相關目錄的全路徑。在iPhone OS上使用這個函數時,第一個參數指定正確的搜索路徑常量,第二個參數則使用NSUserDomainMask常量。表6-2列出了大多數常用的常量及其返回的目錄。
| NSDocumentDirectory | <Application_Home>/Documents |
| NSCachesDirectory | <Application_Home>/Library/Caches |
| NSApplicationSupportDirectory | <Application_Home>/Library/Application Support |
由于NSSearchPathForDirectoriesInDomains函數最初是為Mac OS X設計的,而Mac OS X上可能存在多個這樣的目錄,所以它的返回值是一個路徑數組,而不是單一的路徑。在iPhone OS上,結果數組中應該只包含一個給定目錄的路徑。程序清單6-1顯示了這個函數的典型用法。
程序清單6-1?取得指向應用程序Documents目錄的文件系統路徑
| NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); |
| NSString *documentsDirectory = [paths objectAtIndex:0]; |
在調用NSSearchPathForDirectoriesInDomains函數時,您可以使用NSUserDomainMask之外的其它域掩碼參數,或者使用表6-2之外的其它目錄常量,但是應用程序不能向其返回的目錄寫入數據。舉例來說,如果您指定NSApplicationDirectory作為目錄參數,同時指定NSSystemDomainMask作為域掩碼參數,則可以返回(設備上的)/Applications路徑,但是,您的應用程序不能往該位置寫入任何文件。
另外一個需要記住的考慮是,不同平臺的目錄位置是不一樣的。NSSearchPathForDirectoriesInDomains、NSHomeDirectory、NSTemporaryDirectory、和其它類似函數的返回路徑取決于應用程序運行在設備還是仿真器上。作為例子,程序清單6-1上顯示的函數調用在設備上返回的路徑(documentsDirectory)大致如下:
| /var/mobile/Applications/30B51836-D2DD-43AA-BCB4-9D4DADFED6A2/Documents |
但是,它在仿真器上返回的路徑則具有如下的形式:
| /Volumes/Stuff/Users/johnDoe/Library/Application Support/iPhone Simulator/User/Applications/118086A0-FAAF-4CD4-9A0F-CD5E8D287270/Documents |
在讀寫用戶偏好設置時,請使用NSUserDefaults類或CFPreferences?API。這些接口使您免于構造Library/Preferences/目錄路徑和直接讀寫偏好文件。有關使用這些接口的更多信息,請參見“添加Settings程序包”部分。
如果應用程序的程序包中包含聲音、圖像、或其它資源,則應該使用NSBundle類或CFBundleRef封裝類型來裝載那些資源。程序包知道應用程序內部資源應該在什么位置上,此外,它還知道用戶的語言偏好,能夠自動選擇本地化的資源。有關程序包的更多信息,請參見“應用程序的程序包”部分。
文件數據的讀寫
iPhone OS提供了如下幾種讀、寫、和管理文件的方法:
-
Foundation框架:
-
如果您可以將應用程序數據表示為一個屬性列表,則可以用NSPropertyListSerialization?API來將屬性列表轉換為一個NSData對象,然后通過NSData類的方法將數據對象寫入磁盤。
-
如果應用程序的模型對象采納了NSCoding協議,則可以通過NSKeyedArchiver類、特別是它的archivedDataWithRootObject:方法將模型對象圖進行歸檔。
-
Foundation框架中的NSFileHandle類提供了隨機訪問文件內容的方法。
-
Foundation框架中的NSFileManager類提供了在文件系統中創建和操作文件的方法。
-
-
Core OS調用:
-
諸如fopen、fread、和fwrite這些調用可以用于對文件進行順序或隨機讀寫。
-
mmap和munmap調用是將大文件載入內存并訪問其內容的有效方法。
-
請注意:上面的Core OS調用列表只是列舉一些較為常用的例子。更完全的可用函數列表請參見iPhone OS手冊的第三部分中的函數列表。
本章的下面部分將描述如何使用一些高級技術來進行文件的讀寫。有關Foundation框架中與文件相關類的更多信息,請參見Foundation框架參考。
屬性列表數據的讀寫
屬性列表是一種數據表示形式,用于封裝幾種Foundation(及 Core Foundation)的數據類型,包括字典、數組、字符串、日期、二進制數據、數值及布爾值。屬性列表通常用于存儲結構化的配置數據。舉例來說,每個Cocoa和iPhone應用程序中都有一個Info.plist文件,它就是用于存儲應用程序本身配置信息的屬性列表。您自己也可以用屬性列表來存儲其它信息,比如應用程序退出時的狀態等。
在代碼中,屬性列表的構造通常從構造一個字典或數組、并將它作為容器對象開始,然后在容器中加入其它的屬性列表對象,(可能)包含其它的字典和數組。字典的鍵必須是字符串對象,鍵的值則是NSDictionary、NSArray、NSString、NSDate、NSData、和NSNumber類的實例。
對于可以將數據表示為屬性列表對象的應用程序(比如NSDictionary對象),您可以用程序清單6-2所示的方法來將屬性列表寫入磁盤。該方法將屬性列表序列化為NSData對象,然后調用writeApplicationData:toFile:方法(其實現如程序清單6-4所示)將數據寫入磁盤。
程序清單6-2??將屬性列表對象轉換為NSData對象并寫入存儲
| - (BOOL)writeApplicationPlist:(id)plist toFile:(NSString *)fileName { |
| NSString *error; |
| NSData *pData = [NSPropertyListSerialization dataFromPropertyList:plist format:NSPropertyListBinaryFormat_v1_0 errorDescription:&error]; |
| if (!pData) { |
| NSLog(@"%@", error); |
| return NO; |
| } |
| return ([self writeApplicationData:pData toFile:(NSString *)fileName]); |
| } |
在iPhone OS系統上保存屬性列表文件時,采用二進制格式進行存儲是很重要的。在編碼時,可以通過為dataFromPropertyList:format:errorDescription:方法的format?參數指定NSPropertyListBinaryFormat_v1_0值來實現。二進制格式比其它基于文本的格式緊湊得多,這種緊湊不僅使屬性列表在用戶設備上占用的空間最小,還可以減少讀寫屬性列表的時間。
程序清單6-3的代碼展示了如何從磁盤裝載屬性列表,并重新生成屬性列表中的對象。
程序清單 6-3?從應用程序的Documents目錄讀取屬性列表對象
| - (id)applicationPlistFromFile:(NSString *)fileName { |
| NSData *retData; |
| NSString *error; |
| id retPlist; |
| NSPropertyListFormat format; |
| retData = [self applicationDataFromFile:fileName]; |
| if (!retData) { |
| NSLog(@"Data file not returned."); |
| return nil; |
| } |
| retPlist = [NSPropertyListSerialization propertyListFromData:retData mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&error]; |
| if (!retPlist){ |
| NSLog(@"Plist not returned, error: %@", error); |
| } |
| return retPlist; |
| } |
有關屬性列表和NSPropertyListSerialization類的更多信息,請參見屬性列表編程指南。
用歸檔器進行數據讀寫
歸檔器的作用是將任意的對象集合轉換為字節流。這聽起來像是NSPropertyListSerialization類采用的過程,但它們之間有一個重要的區別。屬性列表序列化只能轉換一個有限集合的數據類型(大多數是數量類型),而歸檔器可以轉換任意的Objective-C對象、數量類型、數組、結構、字符串、及更多其它類型。
歸檔過程的關鍵在于目標對象的本身。歸檔器操作的對象必須遵循NSCoding協議,該協議定義了讀寫對象狀態的接口。歸檔器在編碼一組對象時,會向每個對象發送一個encodeWithCoder:消息,目標對象則在這個方法中將自身的關鍵狀態信息寫入到對應的檔案中。解檔過程的信息流與此相反,在解檔過程中,每個對象都會接收到一個initWithCoder:消息,用于從檔案中讀取當前狀態信息,并基于這些信息進行初始化。解檔過程完成后,字節流就被重新組成一組與之前寫入檔案時具有相同狀態的新對象。
Foundation框架支持兩種歸檔器—順序歸檔和基于鍵的歸檔。基于鍵的歸檔器更加靈活,是應用程序開發中推薦使用的歸檔器。下面的例子顯示如何用一個基于鍵的歸檔器對一個對象圖進行歸檔。_myDataSource對象的representation方法返回一個單獨的對象(可能是一個數組或字典),指向將要包含到檔案中的所有對象,之后該數據對象就被寫入由myFilePath變量指定路徑的文件中。
| NSData *data = [NSKeyedArchiver archivedDataWithRootObject:[_myDataSource representation]]; |
| [data writeToFile:myFilePath atomically:YES]; |
請注意:您還可以向NSKeyedArchiver對象發送archiveRootObject:toFile:消息,以便在一個步驟中完成檔案的創建和將檔案寫入存儲。
您可以簡單地通過相反的流程來裝載磁盤上的檔案內容。在裝載磁盤數據之后,可以通過NSKeyedUnarchiver類及其unarchiveObjectWithData:類方法來取回模型對象圖。例如,您可以用下面的代碼來解檔之前例子中的數據:
| NSData* data = [NSData dataWithContentsOfFile:myFilePath]; |
| id rootObject = [NSKeyedUnarchiver unarchiveObjectWithData:data]; |
更多如何使用歸檔器和如何使對象支持NSCoding協議的信息,請參見Cocoa的歸檔和序列化編程指南。
將數據寫到Documents目錄
有了封裝應用程序數據的NSData對象(或者是檔案,或者是序列化了的屬性列表)之后,您就可以調用程序清單6-4所示的方法來將數據寫到應用程序的Documents目錄中。
程序清單6-4??將數據寫到應用程序的Documents目錄
| - (BOOL)writeApplicationData:(NSData *)data toFile:(NSString *)fileName { |
| NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); |
| NSString *documentsDirectory = [paths objectAtIndex:0]; |
| if (!documentsDirectory) { |
| NSLog(@"Documents directory not found!"); |
| return NO; |
| } |
| NSString *appFile = [documentsDirectory stringByAppendingPathComponent:fileName]; |
| return ([data writeToFile:appFile atomically:YES]); |
| } |
從Documents目錄讀取數據
為了從應用程序的Documents目錄讀取文件,您首先需要根據文件名構建相應的路徑,然后以期望的方法將文件內容讀入內存。對于相對較小的文件—也就是尺寸小于幾個內存頁面的文件—您可以用程序清單6-5中的代碼來取得文件內容。該代碼首先為Documents目錄下的文件構建一個全路徑,并為這個路徑創建一個數據對象,然后返回。
程序清單6-5??從應用程序的Documents目錄讀取數據
| - (NSData *)applicationDataFromFile:(NSString *)fileName { |
| NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); |
| NSString *documentsDirectory = [paths objectAtIndex:0]; |
| NSString *appFile = [documentsDirectory stringByAppendingPathComponent:fileName]; |
| NSData *myData = [[[NSData alloc] initWithContentsOfFile:appFile] autorelease]; |
| return myData; |
| } |
對于載入時需要多個內存頁面的文件,應該避免一次性地裝載整個文件。如果您只是計劃使用部分文件,這一點就尤其重要。對于大文件,您應該考慮用mmap函數或NSData的initWithContentsOfMappedFile:方法來將文件映射到內存。
到底是采用映射文件還是直接裝載取決于您的考慮。如果只需要少量(3-4)內存頁面,則將整個文件載入內存相對安全一些。但是,如果您的文件需要數十或上百個頁面,則將文件映射到內存可能更為有效一些。當然,無論采用什么方法,您都應該測量應用程序的性能,確定裝載文件和為其分配必要內存需要多長時間。
文件訪問的指導原則
在您創建文件或寫入文件數據時,請記住下面這些指導原則:
-
使寫入磁盤的數據量盡可能少。文件操作速度相對較慢,且涉及到Flash盤的寫操作,有一定的壽命限制。下面這些具體的小貼士可以幫助您最少化與文件相關的操作:
-
只寫入發生變化的文件部分,但要盡可能對變化進行累計,避免在只有少數字節發生改變時對整個文件進行寫操作。
-
在定義文件格式時,將頻繁變化的內容放在一起,以便使每次需要寫入磁盤的總塊數最少。
-
如果您的數據是需要隨機訪問的結構化內容,則可以將它們存儲在Core Data持久倉庫或SQLite數據庫中。如果您處理的數據量可能增長到數兆以上,這一點尤其重要。
-
-
避免將緩存文件寫入磁盤。這個原則的唯一例外是:在應用程序退出時,您需要寫入某些狀態信息,使程序在下次啟動時可以回到之前的狀態。
保存狀態信息
當用戶按下Home鍵時,iPhone OS會退出您的應用程序,返回到Home屏幕。類似地,如果您的應用程序打開一個由其它應用程序處理的URI模式,iPhone OS也會退出您的應用程序,在相應的應用程序上打開該URI。換句話說,在Mac OS X上引起應用程序掛起或轉向后臺的動作,在iPhone OS上都會使其退出。這些動作在移動設備上經常發生,因此,您的應用程序必須改變管理可變數據和程序狀態的方式。
大多數桌面應用程序由用戶手工選擇將文件存入磁盤的時機,與此不同的是,iPhone應用程序應該在工作流的關鍵點上自動保存已發生的變化。究竟何時保存數據由您自己來決定,但是有兩個潛在的時間點:或者在用戶做出改變之后馬上進行保存;或者將同一頁面上的變化累計成批,然后在退出該頁面、顯示新頁面、或者應用程序退出的時候進行保存。在任何情況下,您不應該讓用戶漫游到新的頁面而不保存之前頁面的內容。
當您的應用程序被要求退出時,應該將當前狀態保持到臨時的緩存文件或偏好數據庫中。在用戶下次啟動應用程序時,可以根據這些信息將程序恢復到之前的狀態。您保持的狀態信息應該盡可能少,但同時又足夠使應用程序恢復到恰當的點。您不必一定要顯示用戶上次退出時操作的頁面,如果那樣做并不合理的話。比如,如果一個用戶在編輯某個聯系人的時候離開了Phone程序,那么在下次運行時,Phone程序顯示的是聯系人的頂級列表,而不是該聯系人的編輯屏幕。
大小寫敏感性
iPhone OS設備的文件系統是大小寫敏感的。在處理文件名的任何時候,您都應該確保大小寫準確匹配,否則可能不能打開或訪問文件。
轉載于:https://www.cnblogs.com/Camier-myNiuer/p/3381317.html
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的iOS 文件和数据管理 (可能会删除本地文件储存)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: TP-Link TL-WVR450G V
- 下一篇: 批处理之字符串处理和数值计算