<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                > 原文出處:http://chengway.in/post/ji-zhu/core-data-by-tutorials-bi-ji-ba 今天來學習一下多個context的情況,特別是在多線程環境下。第十章也是本書的最后一章,如果你對core data的其他內容感興趣,可以去翻看之前的筆記,或直接購買[《Core Data by Tutorials》](http://www.raywenderlich.com/store/core-data-by-tutorials) ## **Chapter 10: Multiple Managed Object Contexts** 作者一開始介紹了幾種使用多個context的情形,比如會阻塞UI的的任務,最好還是在后臺線程單獨使用一個context,和主線程context分開。還有處理臨時編輯的數據時,使用一個child context也會很有幫助。 ### **一、Getting started** 本章提供了一個沖浪評分的APP作為Start Project,你可以添加沖浪地點的評價,還可以將所有記錄導出為CSV文件。 與之前章節不同的是,這個APP的初始數據存放在**app bundle**中,我們看看在Core Data stack中如何獲取: ~~~ // 1 找到并創建一個URL引用 let seededDatabaseURL = bundle .URLForResource("SurfJournalDatabase", withExtension: "sqlite") // 2 嘗試拷貝seeded database文件到document目錄,只會拷貝一次,存在就會失敗。 var fileManagerError:NSError? = nil let didCopyDatabase = NSFileManager.defaultManager() .copyItemAtURL(seededDatabaseURL!, toURL: storeURL, error: &fileManagerError) // 3 只有拷貝成功才會運行下面方法 if didCopyDatabase { // 4 拷貝smh(shared memory file) fileManagerError = nil let seededSHMURL = bundle .URLForResource("SurfJournalDatabase", withExtension: "sqlite-shm") let shmURL = documentsURL.URLByAppendingPathComponent( "SurfJournalDatabase.sqlite-shm") let didCopySHM = NSFileManager.defaultManager() .copyItemAtURL(seededSHMURL!, toURL: shmURL, error: &fileManagerError) if !didCopySHM { println("Error seeding Core Data: \(fileManagerError)") abort() } // 5 拷貝wal(write-ahead logging file) fileManagerError = nil let walURL = documentsURL.URLByAppendingPathComponent( "SurfJournalDatabase.sqlite-wal") let seededWALURL = bundle .URLForResource("SurfJournalDatabase", withExtension: "sqlite-wal") let didCopyWAL = NSFileManager.defaultManager() .copyItemAtURL(seededWALURL!, toURL: walURL, error: &fileManagerError) if !didCopyWAL { println("Error seeding Core Data: \(fileManagerError)") abort() } println("Seeded Core Data") } // 6 指定store URL即可 var error: NSError? = nil let options = [NSInferMappingModelAutomaticallyOption:true, NSMigratePersistentStoresAutomaticallyOption:true] store = psc.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: options, error: &error) // 7 if store == nil { println("Error adding persistent store: \(error)") abort() } ~~~ 上面的方法除了拷貝sqlite文件,還拷貝了SHM (shared memory file) 和WAL (write-ahead logging) files,這都是為了并行讀寫的需要。無論那個文件出錯了都直接讓程序終止abort。 ### **二、Doing work in the background** 當我們導出數據時,會發現這個過程會阻塞UI。傳統的方法是使用GCD在后臺執行export操作,但Core data managed object contexts并不是線程安全的,也就是說你不能簡單的開啟一個后臺線程然后使用相同的core data stack。 解決方法也很簡單:針對export操作創建一個新的context放到一個私有線程中去執行,而不是在主線程里。 將數據導出為csv,其實很多場景都能用到,具體來學習一下: * 先為實體JournalEntry子類添加一個csv string方法,將屬性輸出為字符串: ~~~ func csv() -> String { let coalescedHeight = height ?? "" let coalescedPeriod = period ?? "" let coalescedWind = wind ?? "" let coalescedLocation = location ?? "" var coalescedRating:String if let rating = rating?.intValue { coalescedRating = String(rating) } else { coalescedRating = "" } return "\(stringForDate()),\(coalescedHeight)," + "\(coalescedPeriod),\(coalescedWind)," + "\(coalescedLocation),\(coalescedRating)\n" } ~~~ * 通過fetch得到所有的jouranlEntry實體,用NSFileManager在臨時文件夾下創建一個csv文件并返回這個URL ~~~ // 1 var fetchRequestError: NSError? = nil let results = coreDataStack.context.executeFetchRequest( self.surfJournalFetchRequest(), error: &fetchRequestError) if results == nil { println("ERROR: \(fetchRequestError)") } // 2 let exportFilePath = NSTemporaryDirectory() + "export.csv" let exportFileURL = NSURL(fileURLWithPath: exportFilePath)! NSFileManager.defaultManager().createFileAtPath( exportFilePath, contents: NSData(), attributes: nil) ~~~ * 用這個URL初始化一個NSFileHandle,用*for-in*遍歷取出每一個journalEntry實體,執行csv()將自身屬性處理成字符串,然后用UTF8-encoded編碼轉換為NSData類型的data,最后NSFileHandle將data寫入URL ~~~ // 3 var fileHandleError: NSError? = nil let fileHandle = NSFileHandle(forWritingToURL: exportFileURL, error: &fileHandleError) if let fileHandle = fileHandle { // 4 for object in results! { let journalEntry = object as JournalEntry fileHandle.seekToEndOfFile() let csvData = journalEntry.csv().dataUsingEncoding( NSUTF8StringEncoding, allowLossyConversion: false) fileHandle.writeData(csvData!) } // 5 fileHandle.closeFile() ~~~ 學習完如何將數據導出為csv,我們來進入本章真正的主題,創建一個私有的后臺線程,把export操作放在這個后臺線程中去執行。 ~~~ // 1 創建一個使用私有線程的context,與main context共用一個persistentStoreCoordinator let privateContext = NSManagedObjectContext( concurrencyType: .PrivateQueueConcurrencyType) privateContext.persistentStoreCoordinator = coreDataStack.context.persistentStoreCoordinator // 2 performBlock這個方法會在context的線程上異步執行block里的內容 privateContext.performBlock { () -> Void in // 3 獲取所有的JournalEntry entities var fetchRequestError:NSError? = nil let results = privateContext.executeFetchRequest( self.surfJournalFetchRequest(), error: &fetchRequestError) if results == nil { println("ERROR: \(fetchRequestError)") } ...... ~~~ 在后臺執行performBlock的過程中,所有UI相關的操作還是要回到主線程中來執行。 ~~~ // 4 dispatch_async(dispatch_get_main_queue(), { () -> Void in self.navigationItem.leftBarButtonItem = self.exportBarButtonItem() println("Export Path: \(exportFilePath)") self.showExportFinishedAlertView(exportFilePath) }) } else { dispatch_async(dispatch_get_main_queue(), { () -> Void in self.navigationItem.leftBarButtonItem = self.exportBarButtonItem() println("ERROR: \(fileHandleError)") }) } } // 5 closing brace for performBlock() ~~~ 關于managed object context的**concurrency types**一共有三種類型: * ConfinementConcurrencyType 這種手動管理線程訪問的基本不用 * PrivateQueueConcurrencyType 指定context將在后臺線程中使用 * MainQueueConcurrencyType 指定context將在主線程中使用,任何UI相關的操作都要使用這一種,包括為table view創建一個fetched results controller。 ### **三、Editing on a scratchpad** 本節介紹了另外一種情形,類似于便箋本,你在上面涂寫,到最后你可以選擇保存也可以選擇丟棄掉。作者使用了一種**child managed object contexts**的方式來模擬這個便簽本,要么發送這些changes到parent context保存,要么直接丟棄掉。 具體的技術細節是:所有的managed object contexts都有一個叫做**parent store**(父母空間)的東西,用來檢索和修改數據(具體數據都是*managed objects*形式)。進一步講,the parent store其實就是一個**persistent store coordinator**,比如main context,他的parent store就是由CoreDataStack提供的*persistent store coordinator*。相對的,你可以將一個context設置為另一個context的**parent store**,其中一個context就是child context。而且當你保存這個child context時,這些changes只能到達parent context,不會再向更高的parent context傳遞(除非parent context save)。 ![](https://box.kancloud.cn/2015-08-21_55d6ecd7b6080.jpg) 關于這個沖浪APP還是有個小問題,當添加了一個新的journal entry后,就會創建新的**object1**添加到context中,如果這時候點擊Cancel按鈕,應用是不會保存到context,但這個**object1**會仍然存在,這個時候,再增加另一個**object2**然后保存到context,此時**object1**這個被取消的對象仍然會出現在table view中。 你可以在cancel的時候通過簡單的刪除操作來解決這個issue,但是如果操作更加復雜還是使用一個臨時的child context更加簡單。 ~~~ // 1 let childContext = NSManagedObjectContext( concurrencyType: .MainQueueConcurrencyType) childContext.parentContext = coreDataStack.context // 2 let childEntry = childContext.objectWithID( surfJournalEntry.objectID) as JournalEntry // 3 detailViewController.journalEntry = childEntry detailViewController.context = childContext detailViewController.delegate = self ~~~ 創建一個childContext,**parent store**設為main context。這里使用了**objectID**來獲取*journal entry*。因為managed objects只特定于自己的context的,而**objectID**針對所有的context都是唯一的,所以childContext要使用**objectID**來獲取mainContext中的*managed objects*。 最后一點要注意的是注釋3,這里同時為detailViewController傳遞了**managed object**(childEntry)和**managed object context**(childContext),為什么不只傳遞**managed object**呢,他可以通過屬性*managed object context*來得到context呀,原因就在于**managed object**對于**context**僅僅是**弱引用**,如果不傳遞context,ARC就有可能將其移除,產生不可控結果。 歷時一周終于寫完了,通過對Core Data的系統學習還是收獲不小的:)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看