iOS - Swift 數據持久化


1、Sandbox 沙箱

  • iOS 為每個應用提供了獨立的文件空間,一個應用只能直接訪問為本應用分配的文件目錄,不可以訪問其他目錄,每個應用自己獨立的訪問空間被稱為該應用的沙盒。也就是說,一個應用與文件系統的交互絕大部分都被限制在它自己的應用沙盒內。

  • 1)在新 App 被安裝時,安裝器會為應用創建一系列角色不同的容器(container)。

    Sandbox1

    • iOS 8.0 之后,bundle 目錄和沙盒目錄 (Data) 是分開的。iOS 7.0 及以前版本 bundle 目錄和沙盒目錄 (Data) 是在一起的。

    • 1> Bundle Container:

      • MyApp.app :這就是應用的運行包(bundle),這個目錄包含應用的可執行文件和所有資源文件。用戶從 App Store 購買應用后,iTunes 安裝到手機上的就是這個目錄文件。這個目錄是只讀的,而且該目錄在安裝時進行了簽名,安裝后對該目錄進行的任何寫操作都會改變目錄的簽名內容,從而使的該應用無法啟動,該應用本身能獲得 bundle 中的任何文件的讀權限。

        	// 獲取包路徑
        
        		let bundlePath:String = NSBundle.mainBundle().bundlePath
        
        		let bundleUrl:NSURL = NSBundle.mainBundle().bundleURL
        
        	// 獲取應用程序程序包中資源文件路徑
        
        		let bundleFilePath:String? = NSBundle.mainBundle().pathForResource("testFile", ofType: "txt")
        
        		let bundleFileUrl:NSURL? = NSBundle.mainBundle().URLForResource("testFile", withExtension: "txt")
        
    • 2> Data Container:

      • iTunes 在與 iPhone 同步時,備份所有的 Documents 和 Library 文件。iPhone 在重啟時,會丟棄所有的 tmp 文件。

      • (1) Documents:

        • 保存由用戶產生的文件或者數據,例如一個日記應用中用戶寫的日記文件,或者音樂播放器中用戶下載的歌曲,塗鴉程序生成的圖片,游戲關卡記錄等。建議將用戶可操作的文件數據都放到該目錄。該文件夾下的內容不會被系統自動刪除,但是嚴格規定必須是用戶自己想要保存的。

        • 該目錄下的所有文件都可以通過 iTunes 進行備份和恢復。存儲在這里的所有文件會自動備份到 iCloud。該目錄下如果保存了從網絡下載的文件,在上架審批的時候,會被拒。

        • Documents/Inbox :該目錄用來訪問被外部應用所請求當前應用打開的文件。例如,我們的應用 MyApp 向系統注冊了可以打開 doc 類型的文件,另一個應用 A 中有一個文件 read.doc ,並申請 MyApp 打開此文件,這時候系統會先將 read.doc 文件拷貝到 MyApp 應用中的 Documents/Inbox 目錄下,MyApp 可以在 Inbox 目錄下進行文件讀取和刪除,但是不能新建文件或者對已有文件進行寫入。如果用戶要對文件進行編輯,需要先將文件移動到其他目錄。

        	// 獲取 Documents 路徑
        
        		let documentPath:String? = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, 
        		                                                               .UserDomainMask, 
        		                                                               true)
        		                                                               .last
        
        		let documentUrl:NSURL? = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, 
        		                                                               inDomains: .UserDomainMask)
        		                                                               .last
        
      • (2) Library:

        • 該目錄用來存儲非用戶數據,它下面包括幾個標准的子目錄,你可以將文件放到其中之一,也可以自己創建子目錄。iOS 應用通常用到的子目錄有 Preferences 和 Caches 。Library 目錄下的文件通常是不應該暴露給用戶的,該目錄下除了 Caches 子目錄外,其它目錄及其文件都可以通過 iTunes 進行備份。

        • Library/Preferences :常用來放置配置文件、數據文件、模板等應用在運行中與用戶相關,而又希望對用戶不可見的文件,如系統偏好設置,用戶偏好設置等文件。使用 NSUserDefaults 類進行偏好設置文件的創建、讀取和修改。

        • Library/Caches :用來存放緩存文件,保存從網絡下載的請求數據,后續仍然需要繼續使用的文件,例如網絡下載的離線數據,圖片,視頻文件等。該目錄中的文件系統不會自動刪除,可以做離線訪問。它的存放時間比 tmp 下的長,但是不如 Library 下的其它目錄。總的來說 Caches 目錄下存放的數據不能是應用程序運行所必需的,但是能提高應用訪問性能的。可寫入應用支持文件,保存應用程序再次啟動需要的信息。

        • iTunes 不會對這個目錄的內容進行備份。要求程序員必需提供一個完善的清除緩存目錄的 "解決方案"。

        	// 獲取 Library 路徑
        
        		let libraryPath:String? = NSSearchPathForDirectoriesInDomains(.LibraryDirectory, 
        		                                                              .UserDomainMask, 
        		                                                              true)
        		                                                              .last
        
        		let libraryUrl:NSURL? = NSFileManager.defaultManager().URLsForDirectory(.LibraryDirectory, 
        		                                                              inDomains: .UserDomainMask)
        		                                                              .last
        
        	// 獲取 Caches 路徑
        
        		let cachesPath:String? = NSSearchPathForDirectoriesInDomains(.CachesDirectory, 
        		                                                             .UserDomainMask, 
        		                                                             true)
        		                                                             .last
        
        		let cachesUrl:NSURL? = NSFileManager.defaultManager().URLsForDirectory(.CachesDirectory, 
        		                                                             inDomains: .UserDomainMask)
        		                                                             .last
        
      • (3) tmp:

        • 該目錄用來存儲臨時文件,那些在下次重啟中不需要再次載入的臨時信息可以存放到該目錄下。在應用不運行時,系統可能會對該目錄下的內容進行清理。

        • 這個目錄的內容不會通過 iTunes 備份。程序員不需要管 tmp 文件夾中的釋放。

        	// 獲取臨時文件路徑
        
        		let tmpPath:String = NSTemporaryDirectory()
        
  • 2)iOS 中同一個應用在不同的手機中分配的路徑可能是不同的,所以我們無法通過硬編碼指定完整路徑名來找到對應文件。Foundation 框架提供了一組專門的接口來獲取應用沙箱不同位置的目錄路徑。

    	// 獲取用戶主路徑
    	public func NSHomeDirectory() -> String
    
    	// 獲取臨時文件路徑
    	public func NSTemporaryDirectory() -> String
    
    	// 獲取滿足條件的路徑列表
    	public func NSSearchPathForDirectoriesInDomains(directory: NSSearchPathDirectory, 
    	                                             _ domainMask: NSSearchPathDomainMask, 
    	                                            _ expandTilde: Bool) -> [String]
    
    • 該函數返回一組路徑的數組,如果僅是查找用戶的目錄,這個數組只包含一個元素,如果第二個參數包含多個值,該數組會包含多個元素。當為 iOS 編寫程序時,第二個參數應是 NSUserDomainMask,並且得到一個包含單個路徑的數組作為返回。在 iOS 中后兩個參數是不變的。

    • directory :指定查找的目錄范圍。

       NSSearchPathDirectory         |                說明
      

      ---------------------------------|-------------------------------------
      ApplicationDirectory | /Applications
      LibraryDirectory | /Library
      DocumentDirectory | /Documents
      ApplicationSupportDirectory | /Library/Application Support
      UserDirectory | /Users
      CachesDirectory | /Library/Caches

    • domainMask :指定查找的文件系統域,可以是多值。

       NSSearchPathDomainMask        |                說明
      

      ---------------------------------|-------------------------------------
      UserDomainMask | 用戶域,指向當前用戶的 home 目錄(~)
      LocalDomainMask | 本地域,指向對所有用戶可用的當前機器范圍
      NetworkDomainMask | 網絡域,指向 /Network
      SystemDomainMask | 系統域,指向 /System

    • expandTilde :指定是否展開路徑中的代字符(~)。

2、NSBundle 路徑

	public class NSBundle : NSObject
  • 在 iOS 中提到的 NSBundle 是文件系統中的一個特殊位置,它存放的是應用可執行文件及相關資源文件。這個位置的所有文件在系統運行時只具有可讀或者可執行的權限,不能進行修改。

  • 應用程序 bundle 中主要有以下幾種類型的文件:

    • Info.plist:用來配置應用的基本參數信息。包括版本號,指向的可執行文件名、包名等。
    • 可執行文件:每個應用程序必須要有一個可執行文件。
    • 資源文件:是可執行文件以外的數據文件,常用的如圖像、圖標、音頻文件、視圖文件、配置文件等。
  • 應用程序 bundle 的配置信息都存在 Info.plist 文件中。它里面各項都以 key-value 的形式進行描述,包括了應用程序唯一標識名、版本號、可執行文件名等信息。

  • bundle 配置信息主要參數:

    key              	          |  友好顯示名                     | value
    

    ------------------------------|-------------------------------|------------------------------------------------------
    CFBundleDisplayName | Bundle display name | 程序的應用名,安裝后顯示在桌面上的那個名稱,可以進行本地化設置
    CFBundleExecutable | Executable file | 應用可執行程序的名稱
    CFBundleIdentifier | Bundle identifier | 應用程序的唯一標識
    CFBundleVersion | Bundle version | 應用編譯版本號
    CFBundleShortVersionString | Bundle version string, short | 應用 release 發布版版本號

  • 當創建一個應用時,系統存儲了應用相關聯的所有數據(其中包括圖片、本地化字符串、圖標等),將這些內容放入一個稱為應用包(application bundle)的包中。在應用中添加一個資源時很方便的:僅需將文件拖到 Xcode 的左邊窗格中。當出現對話框時,通常選擇復制資源文件到你的項目目錄中,這樣你的項目都是自包含的。

  • command-line tool 是沒有 bundle 的,所以用 NSBundle 的時候一直是 null,如果要使用 bundle 獲取項目中的資源,必須要使用 application。

  • Bundle 常見問題

    • 項目里面的某個 .swift 文件無法使用
      • 檢查 TARGETS => Build Phases => Compile Sources 里是否包含此 .swift 文件。
    • 項目里面的某個資源文件(比如 plist、音頻等)無法使用
      • 檢查 TARGETS => Build Phases => Copy Bundle Resources 里是否包含此資源文件。
  • 獲取 NSBundle 資源

    	// 獲得 Bundle 信息
    	/*
    		通常指向 xxx.app/ 這個根目錄
    	*/
    	let mainBundle = NSBundle.mainBundle()
    	 
    	// 獲取 Bundle 文件路徑
    	let bundlePath:String = NSBundle.mainBundle().bundlePath
    	let resourcePath:String? = NSBundle.mainBundle().resourcePath
    	  	
    	// 獲取 Bundle URL 路徑
    	let bundleUrl:NSURL = NSBundle.mainBundle().bundleURL
    	let resourceURL:NSURL? = NSBundle.mainBundle().resourceURL
    	  	
    	// 獲得 Bundle 目錄下的文件路徑
    	
    		let filePath1:String = NSBundle.mainBundle().pathForResource("test", ofType: "txt")!
    		    
    		// bundle 下子目錄 inDirectory 下的文件路徑
    		let filePath2:String = NSBundle.mainBundle().pathForResource("test", ofType: "txt", inDirectory: "testFolder")!
    		   	
    	// 獲得 Bundle 目錄下的 URL 路徑
    		
    		let fileUrl1:NSURL = NSBundle.mainBundle().URLForResource("test", withExtension: "txt")!
    		    
    		// bundle 下子目錄 subdirectory 下的 URL 路徑
    		let fileUrl2:NSURL = NSBundle.mainBundle().URLForResource("test", withExtension: "txt", subdirectory: "testFolder")!
    	  	
    	// 獲取應用程序唯一標識包名
    	let indentifier:String? = NSBundle.mainBundle().bundleIdentifier
    	   	
    	// 獲取應用程序 Info.plist 配置項詞典對象實例
    	let info = NSBundle.mainBundle().infoDictionary
    	  	
    	// 獲取某一特定字段的內容
    	let bundleID:AnyObject? = NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleName")
    

3、NSURL 路徑

	public class NSURL : NSObject, NSSecureCoding, NSCopying, NSURLHandleClient
	public class NSURLComponents : NSObject, NSCopying
  • 在文件系統中,文件和目錄都是使用完整文件路徑來唯一標識的。我們可以使用 NSString 和 NSURL 兩種對象來描述文件路徑。官方建議使用 NSURL 。

  • NSURL 是用來表示 URL 地址的類,通過 URL 我們可以定位一個遠程 web 服務器上的資源位置,也可以定位硬盤上的一個本地文件的路徑。對於 web 資源,往往結合 NSURLSession、NSURLConnection、NSURLDownload 等使用,對於本地資源,一般結合 NSFileManager、NSFileHandle、NSBundle 使用。

  • NSURLComponents 只支持 iOS7.0 及以上的 API 類,它可以實現對 URL 各個部分的讀寫。NSURLComponents 對 URL 各個部分的內容都有兩種格式的屬性來表示:

    原生格式      	|       URL-Encoded
    

    -----------------|-------------------------
    scheme | -
    host | percentEncodedHost
    port | -
    path | percentEncodedPath
    query | percentEncodedQuery
    fragment | percentEncodedFragment
    user | percentEncodedUser
    password | percentEncodedPassword

  • 1)NSURL 結構:

    	protocol://hostname[:port]/path/[;parameters][?query]#fragment        // 帶方括號[]的為可選項
    
    		protocol 	:協議
    		hostname 	:主機名
    		port 	 	:端口
    		path 	 	:路徑
    		parameters  :參數
    		query 		:查詢
    		fragment 	:信息片段
    
    	如:https://swiftinaction.com/example?section=5
    		這個 URL 包含 scheme(https)、host(swiftinaction.com)、path(/example)、query(section=5)等若干部分。
    
    		其中 URL 的 scheme 字段標識了一個 URL 所遵循的協議:
    
    			http://     超文本傳輸協議
    			https://    超文本傳輸安全協議
    			ftp://      文件傳輸協議
    			file://     本地文件 URL
    			data://     數據 URL
    
  • 2)URL 的創建

    	// 由字符串創建
    		    
    		// 簡單字符串直接創建
    		let url1:NSURL = NSURL(string: "https://swiftinaction.com/example")!
    		    
    		let complexString:String = "https://swiftinaction.com/中文字符串/example"
    		let convertString:String? = complexString.stringByAddingPercentEncodingWithAllowedCharacters(
    		                                           NSCharacterSet(charactersInString: "`#%^{}\"[]|\\<> "))
    		    
    		// 由復雜字符串/中文創建時,需要對字符串進行編碼轉換
    		let url2:NSURL = NSURL(string: convertString!)!
    		    
    		// 由基礎 URL 和基於基礎 URL 的一個相對路徑字符串創建
    		let baseUrl:NSURL = NSURL(string: "https://swiftinaction.com")!
    		let fullUrl:NSURL = NSURL(string: "/example", relativeToURL: baseUrl)!
    		    
    		// 生成絕對路徑
    		let url3:NSURL = fullUrl.absoluteURL
    		    
    	// 由文件路徑創建
    		    
    		// 自動判斷 URL 路徑為文件,還是目錄,為目錄時自動在路徑末尾加 “/”
    		let url4:NSURL = NSURL.fileURLWithPath("/Users/JHQ0228/Desktop/test")
    		    
    		// 指定 URL 路徑為一個目錄,自動在路徑末尾加 “/”
    		let url5:NSURL = NSURL.fileURLWithPath("/Users/JHQ0228/Desktop/test", isDirectory: true)
    
  • 3)URL 的獲取

    	let url1:NSURL = NSURL(string: "http://JHQ:password@swiftinaction.com:8080/sayHello/toChinese.png;
    	                                                                 date=20160223?name=JHQ#atMorning")!
    	    
    	// 獲取 URL 的 絕對路徑字符串
    	let absoluteString:String?    = url1.absoluteString
    	
    	// 獲取 URL 的 協議類型
    	let scheneString:String?      = url1.scheme
    	
    	// 獲取 URL 的 主機名
    	let hostString:String?        = url1.host
    	
    	// 獲取 URL 的 端口
    	let portString:NSNumber?      = url1.port
    	
    	// 獲取 URL 的 路徑名
    	let pathString:String?        = url1.path
    	
    	// 獲取 URL 的 參數
    	let parametersString:String?  = url1.parameterString
    	
    	// 獲取 URL 的 查詢語句
    	let queryString:String?       = url1.query
    	
    	// 獲取 URL 的 信息片段語句
    	let fragmentString:String?    = url1.fragment
    	
    	// 獲取 URL 的 路徑組成部分
    	let pathComponents:AnyObject? = url1.pathComponents
    	
    	// 獲取 URL 的 相對路徑
    	let relativePath:String?      = url1.relativePath
    	
    	// 獲取 URL 的 路徑最后一個組成部分
    	let lastPathComponent:String? = url1.lastPathComponent
    	
    	// 獲取 URL 的 路徑擴展名
    	let pathExtension:String?     = url1.pathExtension
    	
    	// 獲取 URL 的 用戶名
    	let user:String?              = url1.user
    	
    	// 獲取 URL 的 密碼
    	let password:String?          = url1.password
    
  • 4)URL 的判斷

    	let fileUrl:NSURL = NSBundle.mainBundle().URLForResource("testFile", withExtension: "txt")!
    	    
    	// 檢查文件的可達性
    	/*
    		只能用來判斷文件系統內部資源的可達性,對於 web URl 等其他類型全返回 false
    	*/
    	let bl1:Bool = fileUrl.checkResourceIsReachableAndReturnError(nil)
    	    
    	// 判斷是否為文件 URl
    	let bl2:Bool = fileUrl.fileURL
    
  • 5)URL 的路徑拼接

    	let url:NSURL = NSURL(string: "http://swiftinaction.com/sayHello")!
    	    
    	// 追加路徑到當前路徑末尾
    		    
    		// 只追加路徑,不在路徑末尾添加 “/”
    		let appendUrl1:NSURL = url.URLByAppendingPathComponent("toJhq")
    		
    		// isDirectory 為 true 時在路徑末尾添加 “/”
    		let appendUrl2:NSURL = url.URLByAppendingPathComponent("toJhq", isDirectory: true)
    		    
    	// 追加路徑后綴到當前路徑末尾
    	/*
    		當路徑以 “/” 結尾時,后綴會添加到末尾的 “/” 之前
    	*/
    	let appendUrl3:NSURL = url.URLByAppendingPathExtension("png")
    	    
    	// 刪除路徑末尾的最后一個路徑
    	let deleteUrl1:NSURL = appendUrl1.URLByDeletingLastPathComponent!
    	    
    	// 刪除路徑末尾的后綴
    	let deleteUrl2:NSURL = appendUrl3.URLByDeletingPathExtension!
    
  • 6)NSURLComponents

    	let componentsUrl:NSURLComponents = NSURLComponents(string: "http://swiftinaction.com/sayHello")!
    	    
    	componentsUrl.scheme = "https"
    	componentsUrl.host = "swiftinaction.com.cn"
    	componentsUrl.port = 8080
    	componentsUrl.path = "/sayHello/toChinese/你好"
    	componentsUrl.query = "name=JHQ"
    	componentsUrl.fragment = "atMorning"
    	    
    	componentsUrl.user = "JHQ"
    	componentsUrl.password = "123456789"
    	    
    	// 將 NSURLComponents 轉為 NSURL
    	let nsUrl:NSURL = componentsUrl.URL!
    	    
    	let componentsUrlString1:String = componentsUrl.path!
    	    
    	// 為內容數據的十六進制編碼字符串
    	let componentsUrlString2:String = componentsUrl.percentEncodedPath!
    

4、數據持久化

  • 在 iOS 開發過程中,不管是做什么應用,都會碰到數據保存的問題。將數據保存到本地,能夠讓程序的運行更加流暢,不會出現讓人厭惡的菊花形狀,使得用戶體驗更好。下面介紹一下數據保存的方式:

    • 1、NSKeyedArchiver

    • 2、NSUserDefaults

    • 3、Write to file

    • 4、SQLite


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM