研究Coredata運作機制(一)
本篇文章主要會從上架計畫的作業文章做研究,內容的部分僅會提到有用到的功能。
Coredata
Coredata是可以用來永久儲存資料的資料庫,目前自己知道這資料庫最棒的部分是可以透過Coredata與SQLite做互動,不太需要知道要如何使用SQLite資料庫(根本是我這個小菜鳥的福音啊!),以及還有人性化的圖形介面可以使用。
CoreData的架構我自己分成三個部分(看到其他文章寫四個或五個),分別為Managed Object Model、Managed Object Context、Persistent Store Coordinator。
Managed Object Model:用來讀取與呈現Core Data Model內的物件結構,Core Data Model是建立CoreData後出現的xcdatamodeld檔案。
在Core Data Model內可以建立實體(Entity)、屬性(Attributes)與資料關聯性(Relationship)等設定。
Managed Object Context:可以在這裡對Managed Object(資料)做儲存、新增、刪除等動作,在使用CoreData存取之前需要使用Managed Object Context,這功能就像一個暫存記憶體(scratch pad memory)用來存取著Managed Object,暫存著一個或多個Managed Object,當接收到儲存指令後才會將Managed Object存取至Persistent Store。
Persistent Store Coordinator:是一個中介的角色,處於Managed Object Model與Persistent Store之間,負責處理資料的讀取與寫入。Persistent Store是實際將資料儲存的地方,是iOS內建的SQLite資料庫。目前自己所知道的Persistent Store儲存方式有四種,分別為:
- SQLite
- XML格式
- 二進位檔(Binary)
- 內存儲存
建立Coredata
在建立App專案時勾選Coredata的選項,如果忘記勾選可以之後在點選NewFile建立。
如果忘記勾選可以透過New File建立
進入專案後可以發現AppDelegate的內容與沒勾選Use Core Data的AppDelegate不太一樣有CoreData的AppDelegate內多了NSPersistentContainer與func saveContext。
NSPersistentContainer是用來簡化NSManagedObjectModel、NSPersistentStoreCoordinator、NSManagedObjectContext的創建與管理。據說在還沒有NSPersistentContainer之前(iOS10),需要個別創建與管理以上三個元件,而NSPersistentContainer簡化了這些步驟。
lazy persistentContainer裡有一個container常數,官方文件寫著這是用來初始化persistentContainer並給一個名稱,這個名稱與.xcdatamodeld檔案名稱是一樣的。一旦初始化persistentContainer就需要透過loadPersistentStores(NSPersistentStoreDescription)來指示container載入persistent stores與建立Core Data stack,如果載入資料時發生錯誤則會自動生出一個NSError的值。
另外lazy的功能就跟字面上的一模一樣,就是當你的另一半或老媽叫你去做家事,而你收到指令才會去做的意思是一樣的。
當資料需要更新、插入與刪除時將會使用到saveContext這功能。
點擊xcdatamodeld檔後建立一個新的實體。
給屬性與型別,這裡比較需要注意的是有些名字系統會擋掉不讓你用。例如上面的someData就是其中之一。
建立完成後要記得按command + B來建立專案,成功建立會跑出Build Succeeded的圖示。
首先插入Coredata套件,並宣告一個NSManagedObject的實體變數
NSFetchedResultsControlloer官方文件是寫著是用來管理Coredata的fetch request回傳的結果(這裡抓取LifeMarker),並將資料顯示給使用者。
NSFetchedResultsControllerDelegate負責更新TableView,文章後面會提到。
let fetchRequest: NSFetchRequest<LifeMarker> = LifeMarker.fetchRequest()
從CoreData(LifeMarker)取得NSFetchRequest,透過NSFetchRequest用來取得persistent store內的實體資料。
let sortDescriptor = NSSortDescriptor(key: "timerTitle", ascending: true)
NSSortDescriptor是用來排列表格順序,這裡指定了『timerTitle』作為排列的依據。
let context = appDelegate.persistentContainer.viewContext
從appDelegate內取出viewContext。接著初始化NSFetchedResultsController並填入參數,這裡填入已經設定好的fetchRequest與context,cacheName與sectionNameKeyPath是屬於選填,因此這裡設定為nil。
接著將fetchedResultsController的delegate設定為self。這裡的self指的就是FetchedResultsControllerDelegate。
如果沒有在class插入協定,這裡會發生錯誤並詢問是否插入協定。
接著透過performFetch執行fetchRequest,由於performFetch的function裡有throws,意味著這裡可能丟出錯誤,因此需要使用到try去試是否會出錯,並藉由catch抓取錯誤。
新增資料
新增資料由另一個頁面按下按鈕後新增資料,使用appDelegate內的saveContext儲存資料。
主頁面表格資料更新
當發現Managed Object Context內的資料有變動時,fetchedRequest會通知fetchedResultsControlle有變動,接著透過NSFetchedResultsControllerDelegate來更新Table View畫面,不過需要注意的是需要至少一個delegate method才能更新畫面。
而官方文件是寫著通常會使用到controllerWillChangeContent(_:) 和controllerDidChangeContent(_:)
controllerWillChangeContent(_:)是用來通知接收者(Table View)fetched results controller準備開始進行增加、移動、刪除、更新其一或多個動作。
controller(_:didChange:at:for:newIndexPath:)用來通知接收者由於Managed Object Context有增加、移動、刪除、更新的其一或多個動作,因此fetched object已經被更改。fetchedObject就是前面已經設定的timePageData。
controllerDidChangeContent(_:)用來通知接收者fetched results controller完成了增加、移動、刪除、更新其一或多個動作。
為了瞭解清楚整個CoreData運作方式,查閱了手邊的幾本書籍與網路資料,但感覺大部分的資料沒有講解的很詳細或是只講解一部分而已,於是開始研究每個物件的功能與彼此之間的關係,以及猜測CoreData整體實際運行的方式。因此本篇文章可能會有部分的內容不是正確或完整的。這部分日後能力進步到一個階段後會再來修改或新增文章。