原文:Three Common Core Data Mistakes to Avoid
作者: Bart Jacobs
譯者: CocoaChina-ydatong(簡書)
熟悉 Core Data 的開發者並不認為這個框架用起來很難。如果你遵循這個框架的規則,Core Data 不會讓你感到困惑。但通常這也是個問題,開發者犯錯往往是因為違反了某條他們並不知道的規則。所以,這篇文章我將主要討論開發者在使用Core Data時常犯的三個錯誤。
1.在錯誤的線程訪問托管對象上下文(Managed Object Context)
Core Data從未被設計用來在多線程的環境中使用。幸運的是,這個框架已經在這些年逐漸發展,使得 Core Data 在多線程的應用程序中使用並不困難。
如果你熟悉Core Data,那你應該了解Core Data使用 線程限制 來保護托管對象上下文。這意味著你只應該通過與托管對象上下文所關聯的線程來訪問它。但是你怎麼知道與之關聯的是哪一個線程呢?
在iOS 5和macOS 10.7中,一個托管對象上下文創建並且管理一個調度隊列,並在這個隊列上進行工作。`NSManagedObjectContext` 類提供了一個方便的接口來在托管對象上下文創建的調度隊列中執行各種操作。你有兩個選擇:
perform(_:)
performAndWait(_:)
每個方法都接收一個閉包作為參數,可以在這個閉包中對托管對象上下文所管理的托管對象進行操作。
managedObjectContext.perform { ... } managedObjectContext.performAndWait { ... }
`perform(_:)` 和 `performAndWait(_:)` 的區別在於托管對象上下文的調度隊列如何來計劃執行閉包中的操作。
如名字所示
`perform(_:)` 方法在托管對象上下文的調度隊列中,異步執行閉包中的操作。
`performAndWait(_:)` 方法在托管對象上下文的調度隊列中,同步執行閉包中的操作。
除非你完全確定你在使用正確的線程訪問托管對象上下文,否則你應該使用 `perform(_:)` 或者 `performAndWait(_:)` 來避免遇到線程問題。
2.跨線程傳遞托管對象
另外一個開發者常犯的錯誤是跨線程傳遞托管對象。比如,當應用程序從遠程後端獲取數據時,這一錯誤並不罕見。你需要遵循的規則很簡單,你永遠都不應該將一個 `NSManagedObject` 對象從一個線程傳遞到另一個線程。`NSManagedObject` 類並不是線程安全的。
然而這看上去似乎很不方便。解決方案很簡單,Core Data 框架提供了一個跨線程傳遞托管對象的解決方案: `NSManagedObjectID` 類。這個類的一個實例唯一標識程序中
某一個托管對象。重要的是,`NSManagedObjectID`這個類是線程安全的。
你不應該再從一個線程傳遞托管對象到另一個線程,而是傳遞 `NSManagedObjectID` 對象。你可以通過一個 `NSManagedObject` 的 `objectID` 屬性來獲取它的唯一標識。
let objectID = managedObject.objectID
托管對象上下文知道如何通過你傳遞的 `NSManagedObjectID` 對象來獲取對應的托管對象。事實上,`NSManagedObjectContext` 類提供了好幾個方法來根據`NSManagedObjectID` 對象獲取對應的托管對象。
object(with:)
existingObject(with:)
registeredObject(for:)
每個方法都接收一個 `NSMangedObjectID` 對象作為參數。
let objectID = managedObject.objectID DispatchQueue.main.async { ... let managedObject = mainManagedObjectContext.object(with: objectID) ... }
第一個方法, `object(with:)` ,返回一個與 `NSManagedObjectID` 實例對應的托管對象。如果托管對象上下文中不包含一個與這個標識對應的托管對象,它就會去持久化存儲協調器(Persistent store coordinator)中查找。這個方法始終返回一個托管對象。
要了解的是,`object(with:)` 方法在找不到與唯一標識對應的托管對象時,會拋出異常。比如,如果應用程序刪除了與唯一標識對應的記錄,Core Data 不能夠返回對應的托管對象,結果就是拋出一個異常。
`existingObject(with:)` 方法與之類似。主要的區別是,如果托管對象上下文找不到與這個唯一標識對應的托管對象時,會拋出一個錯誤。
第三個方法,`registeredObject(for:)` 只有在你所要獲取的托管對象已經在托管對象上下文中注冊過,才會返回這個托管對象。換而言之,返回值是可選類型的 `NSManagedObject?` 。如果托管對象上下文在持久化存儲協調器中找不到對應的記錄,它將不會返回托管對象。
托管對象的標識與數據庫中的對應記錄的主鍵很相似,但是並不完全相同。它唯一標識這條記錄,並且使得你的應用程序可以獲取到某條指定的記錄,而不用去管當前的操作是在哪個線程。
3.忽略基礎知識
目前為止,開發者最常犯的錯誤,應該是忽略了 Core Data 的基本原理。開發者沒有花時間去研究其中的原理,所以在碰到問題或者得到與自己期望不一樣的結果時會感到困惑。
別糾結,慢慢來
多花點時間學習 Core Data,能減少你很多困惑和浪費的時間。Core Data 不是 UIKit 或者 Foundation 。你不能只選擇去學習你想使用的部分。這篇文章介紹了 Core Data 有一些十分嚴格的規則你需要去遵循。如果你花點時間來學習這些規則,你會受益匪淺。
如果你下定決心要學好Core Data,那你可以選擇我的課程 [Mastering Core Data With Swift 3] 。不到三個小時的時間,你就可以學習到如何把Core Data整合到你的項目中去。Core Data並不難,這一點我可以向你保證。