研究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儲存方式有四種,分別為:

  1. SQLite
  2. XML格式
  3. 二進位檔(Binary)
  4. 內存儲存
https://developer.apple.com/documentation/coredata/core_data_stack

建立Coredata

在建立App專案時勾選Coredata的選項,如果忘記勾選可以之後在點選NewFile建立。

如果忘記勾選可以透過New File建立

進入專案後可以發現AppDelegate的內容與沒勾選Use Core Data的AppDelegate不太一樣有CoreData的AppDelegate內多了NSPersistentContainer與func saveContext。

NSPersistentContainer是用來簡化NSManagedObjectModel、NSPersistentStoreCoordinator、NSManagedObjectContext的創建與管理。據說在還沒有NSPersistentContainer之前(iOS10),需要個別創建與管理以上三個元件,而NSPersistentContainer簡化了這些步驟。

https://developer.apple.com/documentation/coredata/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。

https://developer.apple.com/documentation/coredata/nsfetchedresultscontroller

接著將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才能更新畫面。

https://developer.apple.com/documentation/coredata/nsfetchedresultscontroller#1661453

而官方文件是寫著通常會使用到controllerWillChangeContent(_:) 和controllerDidChangeContent(_:)

https://developer.apple.com/documentation/coredata/nsfetchedresultscontrollerdelegate

controllerWillChangeContent(_:)是用來通知接收者(Table View)fetched results controller準備開始進行增加、移動、刪除、更新其一或多個動作。

controller(_:didChange:at:for:newIndexPath:)用來通知接收者由於Managed Object Context有增加、移動、刪除、更新的其一或多個動作,因此fetched object已經被更改。fetchedObject就是前面已經設定的timePageData。

controllerDidChangeContent(_:)用來通知接收者fetched results controller完成了增加、移動、刪除、更新其一或多個動作。

--

--