【說明】
此份筆記是根據ISBN:978-986-201-900-9這本書的第18章節所紀錄的。
Core Data與plist的差異:
plist處理小資料量時比較快速,但是會一次把文件載入記憶體內,效率上不是很優。Core Data擁有table之間的Relationship,與SQLite相似,但是不必下語法。
Core Data就是可以儲存到磁碟的物件圖,不僅僅在磁碟上儲存資料,也把我們需要的資料物件讀取到記憶體中。
- Marcus Zarra, Core Data
Core Data主要任務是負責資料更改的管理,序列化到磁碟,最小化記憶體佔用,以及查詢資料,可以將資料儲存為XML、二進制檔案或是SQLite檔案。
Core Data堆疊(Stack):
託管物件模型(Managed Object Model):描述你在程式裡使用的Schema(綱要)。
永續性儲存協調器(Persistent Store Coordinator):負責處理不同永續性物件儲存區以及將物件儲存到儲存區中。
託管物件本文(Managed Object Context):管理被建立的物件並使用Core Data回傳。
【專案開發步驟】
建立專案:
使用Empty Application模板建立專案,專案名稱設為RecipeStore,並勾選使用Core Data。
因為使用Empty Application建立專案,所以需新增Storyboard來方便我們撰寫程式,並將名稱設為Main,如下圖所示。
|
新增Storyboard |
點選專案,將Main Interface設為Main,如下圖所示。
|
設定Main Interface |
將didFinishLaunchingWithOptions:這個方法更新,如下所示。
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
return true
}
定義託管物件模型:
在資料模型內建立一個Entity,並取名為Recipe,如下圖所示。
|
建立Entity |
在名為Recipe的Entity內建立三個Attributes,分別為name、image、prepTime,並設三個資料型態為String,如下圖所示。
|
建立Attributes |
設計使用者介面:
依照下圖建立好介面。
|
建立使用者介面 |
將+的Segue Identifier設為Add,如下圖所示。
|
設定Segue Identifier |
並將Prototype Cell的Identifier設為Cell,如下圖所示。
|
設定Identifier為Cell |
建立一個TableViewController的類別,取名為RecipeStoreTableViewController,並繼承UITableViewController,至Storyboard設定介面與自訂類別的關聯,如下圖所示。
|
設定頁面與自訂類別的關聯 |
建立一個ViewController的類別,取名為AddRecipeViewController,並繼承UIViewController,至Storyboard設定介面與自訂類別的關聯,如下圖所示。
|
設定頁面與自訂類別的關聯 |
建立AddRecipeViewController頁面上,三個TextField與兩個Bar Button的IBOutlet與IBAction,如下圖所示。
|
建立物件的關聯 |
建立託管物件:
在AppRecipeViewController.swift內建立一個func,如下所示。
func managedObjectContext() -> NSManagedObjectContext {
let appDelegate: AppDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
let context: NSManagedObjectContext = appDelegate.managedObjectContext
return context
}
透過這個func可以管理物件,經由本文就可以取得或變更託管物件。
在AppRecipeViewController.swift實作save:方法,如下所示。
@IBAction func save(sender: UIBarButtonItem) {
var context: NSManagedObjectContext = self.managedObjectContext()
var newRecipe: NSManagedObject = NSEntityDescription.insertNewObjectForEntityForName("Recipe", inManagedObjectContext: context) as NSManagedObject
newRecipe.setValue(nameTextField.text, forKey: "name")
newRecipe.setValue(imageTextField.text, forKey: "image")
newRecipe.setValue(prepTimeTextField.text, forKey: "prepTime")
self.dismissViewControllerAnimated(true, completion: nil)
}
先取得託管物件本文,然後針對新的食譜建立一個NSManagedObject,並將託管物件跟Entity關聯在一起。NSManagedObject就像是NSDictionary一樣,一旦建立託管物件(newRecipe)後,即可將Entity的屬性當作Key一樣來設定value。最後dismissViewControllerAnimated用來解除目前的ViewController。
在AppRecipeViewController.swift實作cancel:方法,如下所示。
@IBAction func cancel(sender: UIBarButtonItem) {
self.dismissViewControllerAnimated(true, completion: nil)
}
解除目前的ViewController。
取得託管物件:
在RecipeStoreTableViewController.swift內建立一個名為recipes的可變陣列,如下所示。
var recipes: NSMutableArray = []
資料型態為NSMutableArray的陣列變數,名為recipes。
在RecipeStoreTableViewController.swift內建立一個func,取得託管物件文本,與AppRecipeViewController.swift內的func相同。
在RecipeStoreTableViewController.swift內增加viewDidAppear方法,如下所示。
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(true)
var managedObjectContext = self.managedObjectContext()
let fetchRequest = NSFetchRequest(entityName: "Recipe")
recipes = NSMutableArray(array: managedObjectContext.executeFetchRequest(fetchRequest, error: nil))
self.tableView.reloadData()
}
為了要取得資料,必須取得本文(Context)。建立一個提取需求(fetch request),並指定要去提取的Entity。利用Recipe entity建立一個NSFetchRequest的實體,並用executeFetchRequest:這個方法取得資料庫的所有食譜。最後讓tableView重新載入。
viewDidAppear方法是在View確實被顯示出來時才會呼叫的,利用這個方法讓新食譜被建立後重新載入資料,但這不是好的方法,因為若使用者不想新建食譜時按下取消也會被呼叫。
在TableView中填入食譜:
在RecipeStoreTableViewController.swift內實作以下方法。
override func numberOfSectionsInTableView(tableView: UITableView!) -> Int {
return 1
}
回傳有幾個Section在TableView裏面。
override func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
return recipes.count
}
告訴ViewController要顯示多少個Cell。
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell? {
var cellIdentifier: String = "Cell"
var cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as UITableViewCell
if cell == nil {
cell = UITableViewCell(style: .Default, reuseIdentifier: cellIdentifier)
}
var recipe: NSManagedObject = recipes.objectAtIndex(indexPath.row) as NSManagedObject
cell.textLabel.text = recipe.valueForKey("name") as String
var imageString: String = recipe.valueForKey("image") as String
var prepTimeString: String = recipe.valueForKey("prepTime") as String
cell.detailTextLabel.text = "\(imageString) - \(prepTimeString)"
return cell
}
在TableView內顯示資料,可參考
這篇。NSManagedObject與NSDictionary非常像,使用valueForKey方法就可以取得屬性值。將食譜名稱作為標題,圖檔名與準備時間以子標題方式呈現。
【執行結果】
【專案範例】