我把CoreData叫做大寶劍像屋,為什么呢佑惠,因?yàn)镃oreData用起來著實(shí)讓人感到舒爽~~這一篇讓我們來詳細(xì)的了解一下大寶劍~
- 參考書籍:CORE DATA by Tutorials
- 默認(rèn)有swift基礎(chǔ)幅虑。
- 默認(rèn)已閱讀上一篇內(nèi)容涨醋。
這一篇主要內(nèi)容:
- 深入了解CoreData對(duì)象果善;
- 創(chuàng)建自己的棧都办;
- data model中的Relationships屬性烙荷;
- 刪除數(shù)據(jù)镜会;
(1)、深入了解CoreData對(duì)象
之前在我們創(chuàng)建工程的時(shí)候勾選了Use CoreData的選擇框终抽,勾選這個(gè)選擇框以后在AppDelegate.swift中自動(dòng)生成了以下四個(gè)對(duì)象:
- NSManagedObjectModel
- NSPersistentStore
- NSPersistentStoreCoordinator
- NSManagedObjectContext
在這一篇中我們不勾選這個(gè)選擇框戳表,自己添加這四個(gè)類以便于詳細(xì)學(xué)習(xí)“大寶劍”。像這樣:
NSManagedObjectModel是什么昼伴?
這個(gè)對(duì)象就是你的data model匾旭,代表了里面的每一個(gè)對(duì)象類型。
你可以把他當(dāng)作是你的數(shù)據(jù)庫(kù)的圖形化顯示圃郊。呼呼~~和“度娘”的解釋差不多价涝。
Note:如何將該對(duì)象與data model聯(lián)系起來?
來看這個(gè)方法:
let modelURL = NSBundle.mainBundle().URLForResource("CoreDataTest3", withExtension: "momd")!
通過這個(gè)方法持舆,編譯器將data model文件編譯以后放入了一個(gè).momd文件夾色瘩,并返回了這個(gè)文件夾的地址,通過這個(gè)地址我們初始化了我們的NSManagedObjectModel對(duì)象逸寓。
NSPersistentStore是什么居兆?
無論你決定使用哪種方法來進(jìn)行存儲(chǔ)你都得使用NSPersistentStore來進(jìn)行存儲(chǔ)或者讀寫數(shù)據(jù)。CoreData提供了三種原子操作對(duì)象和一種非原子操作對(duì)象竹伸。
原子操作對(duì)象在你操作任何讀寫操作之前將數(shù)據(jù)全部讀寫到內(nèi)存中泥栖,非原子操作對(duì)象則相反,在需要的時(shí)候加載數(shù)據(jù)勋篓。
來看看這四種NSPersistentStore對(duì)象
- NSQLiteStoreType支持SQLite數(shù)據(jù)庫(kù)吧享。他是CoreData中唯一的非原子操作存儲(chǔ)類型。這基本是絕大多數(shù)應(yīng)用最好的選擇了譬嚣,xcode在默認(rèn)狀態(tài)下使用的就是這種存儲(chǔ)類型钢颂。
- NSXMLStoreType支持XML文件,這是一種可讀的存儲(chǔ)類型孤荣。這是一種原子操作類型甸陌,只在OS X上使用
- NSBinaryStoreType支持二進(jìn)制文件须揣,原子操作類型,很少在項(xiàng)目中用到钱豁。
- NSInMemoryStoreType是一個(gè)對(duì)內(nèi)存中的數(shù)據(jù)進(jìn)行存儲(chǔ)的類型耻卡,所以這并不是一個(gè)正真的“持久化”存儲(chǔ)類型。存儲(chǔ)在內(nèi)存中有助于測(cè)試牲尺,但并沒有做到數(shù)據(jù)持久化卵酪。
NSPersistentStoreCoordinator是什么?
這是NSManagedObjectModel和NSPersistentStore之間的橋梁谤碳。他能夠向NSManagedObjectModel發(fā)送信息并存儲(chǔ)溃卡,也能夠從NSPersistentStore中讀取信息。
NSManagedObjectContext是什么蜒简?
這是我們之前唯一見到過的一個(gè)類型了瘸羡。我們已經(jīng)提到過這些特性(更多特性后面的篇章會(huì)提到):
- 在內(nèi)存中也就是我們一直說的‘暫存器’中工作。
- 如同前面所說CoreData的任何操作的第一步就是創(chuàng)建NSManagedObjectContext對(duì)象搓茬。
- 在你使用save()方法之前犹赖,你的任何改變都不會(huì)影響我們磁盤中的數(shù)據(jù)
以上內(nèi)容不了解也沒事,因?yàn)槲覀儠?huì)一個(gè)一個(gè)用到的卷仑。
(2)峻村、自己來創(chuàng)建一個(gè)棧
創(chuàng)建一個(gè)新文件CoreDataStack.swift(在這個(gè)文件中,我們將自己添加全部之前xcode自動(dòng)生成的代碼锡凝,以便于對(duì)那四個(gè)對(duì)象的理解)粘昨,添加以下代碼:
<pre><code>
import CoreData
class CoreDataStack {
let context:NSManagedObjectContext!
let psc:NSPersistentStoreCoordinator!
let model:NSManagedObjectModel!
let store:NSPersistentStore!
init() {
//1
let bundle = NSBundle.mainBundle()
let modelURL = bundle.URLForResource("CoreDataTest3", withExtension:"momd")
model = NSManagedObjectModel(contentsOfURL: modelURL!)
//2
psc = NSPersistentStoreCoordinator(managedObjectModel:model)
//3
context = NSManagedObjectContext()
context.persistentStoreCoordinator = psc
//4
let fileManager = NSFileManager.defaultManager()
let urls = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask) as! [NSURL]
let documentsURL = urls[0]
let storeURL = documentsURL.URLByAppendingPathComponent("Dog Walk")
let options = [NSMigratePersistentStoresAutomaticallyOption: true]
var error: NSError? = nil
store = psc.addPersistentStoreWithType(NSSQLiteStoreType,
configuration: nil, URL: storeURL, options: options, error:&error)
if store == nil {
println("Error adding persistent store: \(error)")
abort()
}
}
func saveContext() {
var error: NSError? = nil
if context.hasChanges && !context.save(&error) {
println("Could not save: \(error), \(error?.userInfo)") }
}
}
}
</code></pre>
一下子添加了這么一大塊代碼,一眼看過去一定是暈暈的窜锯,好的张肾,一句一句來解釋一下吧。
- 首先添加了四個(gè)常量衬浑,這四個(gè)常量就是我們之前講了很久的那四個(gè)CoreData的對(duì)象捌浩,而接下來的初始化方法就是對(duì)這四個(gè)常量進(jìn)行初始化放刨。
- 初始化函數(shù)工秩。
- //1 前面已經(jīng)說過了NSManagedObjectModel代表著我們的data model。
let modelURL = bundle.URLForResource("CoreDataTest3", withExtension:"momd")
這個(gè)方法將data model編譯成一個(gè)‘momd’文件进统,并返回他的地址助币,NSManagedObjectModel對(duì)象則是通過這個(gè)地址來進(jìn)行初始化。 - //2 對(duì)NSPersistentStoreCoordinator進(jìn)行初始化螟碎,他是data model與‘暫存器’NSManagedObjectContext之間的橋梁眉菱。
- //3 對(duì)‘暫存器’NSManagedObjectContext進(jìn)行初始化,并與我們的‘橋梁’連接起來掉分。
- //4 通過
func addPersistentStoreWithType(storeType: String, configuration: String?, URL storeURL: NSURL?, options: [NSObject : AnyObject]?, error: NSErrorPointer) -> NSPersistentStore?
方法對(duì)NSPersistentStore進(jìn)行初始化俭缓,在這里我們指定了persistent store類型克伊。 - 最后我們添加了保存方法,也就是將‘暫存器’context保存到磁盤中华坦,代碼很容易理解愿吹,就不進(jìn)行解釋了。
以上添加的代碼仔細(xì)觀察的話惜姐,很容易就會(huì)發(fā)現(xiàn)基本就是在我們勾選了 use core data 以后xcode自動(dòng)生成的代碼犁跪,而我們只是將它寫在了一個(gè)類中,以便于觀察及理解歹袁。
像我們上面那樣創(chuàng)建了一個(gè)類坷衍,但是這個(gè)類根本沒有參與到我們的程序中來,接下來就是將這個(gè)類參與到程序中条舔。
打開AppDelegate.swift枫耳,添加以下代碼:
import CoreData
lazy var coreDataStack = CoreDataStack()
如此一來coreDataStack這個(gè)變量就擁有了我們之前寫的四個(gè)對(duì)象了。
在下面兩個(gè)方法中添加coreDataStack.saveContext():
<pre><code>
func applicationDidEnterBackground(application: UIApplication) {
coreDataStack.saveContext()
}
func applicationWillTerminate(application: UIApplication) {
coreDataStack.saveContext()
}
</code></pre>
這倆方法確定應(yīng)用在發(fā)生意外退出的時(shí)候保存數(shù)據(jù)孟抗,使數(shù)據(jù)不會(huì)丟失嘉涌。
再來觀察一下我們的文件目錄,哦~我們還差一個(gè).xcdatamodeld文件夸浅,那么就來新建一個(gè)吧仑最。
右擊文件目錄->New file...->選擇iOS Core Data->選擇Data Model->next->命名為‘CoreDataTest3’,像這樣:
現(xiàn)在來看我們的程序就和勾選了use core data選擇一模一樣了帆喇,接下來就可以進(jìn)行前面兩篇的操作警医,而且效果相同。哦~唯一的區(qū)別就是使用coreDataStack.context來替代前面篇章中AppDelegate.swift中的managedObjectContext坯钦。
(3)预皇、relationships屬性和刪除功能
接下來的內(nèi)容婉刀,我們通過一個(gè)Demo吟温,來演示一下data model中的relationships屬性和刪除功能突颊。
打開storyboard刪除原有的控制器鲁豪,拖入一個(gè)tableviewcontroller,并將之設(shè)置為程序人口律秃,如圖所示:
再為這個(gè)控制器創(chuàng)建一個(gè)類爬橡,如以下步驟:
New File->選擇iOS Source,繼續(xù)選擇cocoa touch class->next->將其選擇為UITableviewController的子類->next->Create
同時(shí)將控制器和我們新建的文件進(jìn)行綁定棒动。
將控制器轉(zhuǎn)化為Navigationcontroller糙申,日?qǐng)D操作:
給navigation Bar添加一個(gè)right Button,并添加動(dòng)作‘a(chǎn)ddTime’船惨,添加代碼柜裸。
將實(shí)現(xiàn)以下功能缕陕,由于這段實(shí)現(xiàn)并不復(fù)雜,在這里就不進(jìn)行描述疙挺,有問題的可以留言榄檬。
目前這個(gè)版本并沒有實(shí)現(xiàn)數(shù)據(jù)持久化衔统,當(dāng)我們退出應(yīng)用以后數(shù)據(jù)將消失鹿榜,接下來我們做的就是將數(shù)據(jù)持久化,這聽起來好像在前兩篇我們已經(jīng)做過了锦爵,不然~~~在這里我們將引進(jìn)relationships的概念舱殿,同時(shí)實(shí)現(xiàn)刪除功能。
- relationships
這個(gè)邏輯是這樣的险掀,我們需要一個(gè)數(shù)組來存放時(shí)間沪袭,我們暫且把這個(gè)數(shù)組叫做‘時(shí)間組’,這個(gè)‘時(shí)間組’存放了許多時(shí)間樟氢,那么這個(gè)時(shí)間組就是一個(gè)Entity冈绊,里面存放的時(shí)間也是Entity,不過時(shí)間組這個(gè)Entity的每一個(gè)對(duì)象都擁有很多的時(shí)間Entity埠啃,先來創(chuàng)建這兩個(gè)Entity死宣,一個(gè)起名為“TimeArry”,一個(gè)起名為“Time”碴开,在"Time"這個(gè)Entity中有一個(gè)屬性time類型選擇為NSDate:
而在“TimeArry”中則存在一個(gè)relationship毅该,起名為“times”,指向“Time”Entity:
Note:relationship生成的屬性是什么類型的潦牛?“To Many”生成的是NSSet類型眶掌,如果想使用下標(biāo)來來使用對(duì)象的話,請(qǐng)?jiān)谟疫叺膶傩詸谥泄催xOrdered選項(xiàng)巴碗,當(dāng)你勾選了這個(gè)選項(xiàng)以后生成的“To Many”類型就是NSOrderedSet類型朴爬。
在這里我們使用NSOrderedSet類型
同時(shí)將Type選擇為“To Many”,勾選Ordered橡淆。
給我們的兩個(gè)Entity生成對(duì)象類吧召噩,生成方法在上一篇中已經(jīng)講過:
Editor—>Create NSManagedObject Subclass .............
首先要添加的代碼當(dāng)然是addTime方法來添加數(shù)據(jù),在此方法中添加以下代碼:
<pre><code>
@IBAction func addTime(sender: AnyObject) {
//獲取當(dāng)前時(shí)間
let date=NSDate()
//1
let entity = NSEntityDescription.entityForName("Time", inManagedObjectContext: managedContext)
let TimeObject = Time(entity: entity!, insertIntoManagedObjectContext: managedContext)
TimeObject.time=date
//2 Insert the new times into the TimeArry's times set
var times = timearry.times.mutableCopy() as! NSMutableOrderedSet
times.addObject(TimeObject)
timearry.times = times.copy() as! NSOrderedSet
//3 Save the managed object context
var error: NSError?
if !managedContext!.save(&error) {
println("Could not save: \(error)")
}
//4
let timeFetch = NSFetchRequest(entityName: "TimeArry")
let result = managedContext.executeFetchRequest(timeFetch, error: &error) as! [TimeArry]!
self.timearry=result[0]
self.tableview.reloadData()
}
</code></pre>
代碼解釋:
- //1 初始化一個(gè)“Time”對(duì)象實(shí)例明垢。
- //2
- 初始化times屬性
- 將之前的“Time”對(duì)象實(shí)例
- 添加到relationships中
- 將relationship添加到當(dāng)前顯示的“TimeArry”
- //3 保存數(shù)據(jù)
- //4 更新timearry數(shù)組
Note:timearry就是我們?cè)诮缑嫔巷@示的“TimeArry”對(duì)象的一個(gè)實(shí)例蚣常,按理來說TimeArry有很多個(gè)對(duì)象市咽,每一個(gè)“TimeArry”對(duì)象都有自己的“Time”痊银,這樣說起來好像很像一個(gè)二維數(shù)組,而事實(shí)上我們只顯示了這個(gè)二維數(shù)組的第一行施绎。那么在進(jìn)入程序的時(shí)候我們就得創(chuàng)建這個(gè)timearry對(duì)象溯革。
添加一下代碼:
<pre><code>
var timearry:TimeArry!
override func viewDidLoad() {
super.viewDidLoad()
//初始化暫存器
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
managedContext = appDelegate.coreDataStack.context
//1 獲取“TimeArry”對(duì)象贞绳,并初始化timearry
var error: NSError?
let timeFetch = NSFetchRequest(entityName: "TimeArry")
let result = managedContext.executeFetchRequest(timeFetch, error: &error) as! [TimeArry]!
if result.count == 0 {
let entity = NSEntityDescription.entityForName("TimeArry", inManagedObjectContext: managedContext)
self.timearry = TimeArry(entity: entity!, insertIntoManagedObjectContext: managedContext)
}else{
self.timearry=result[0]
}
// 添加Edit按鈕
self.navigationItem.leftBarButtonItem = self.editButtonItem()
}
</code></pre>
這段代碼很容易理解(如果你看了我前面的兩篇內(nèi)容的話),就是讀取了“TimeArry”的內(nèi)容致稀,然后如果存在數(shù)據(jù)冈闭,則使timearry為第一組數(shù)據(jù),若不存在數(shù)據(jù)則初始化抖单。
因?yàn)槲覀兪怯靡粋€(gè)tableview來顯示數(shù)據(jù)萎攒,所以添加以下代碼:
<pre><code>
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.timearry.times.count
}
</code></pre>
就不對(duì)這段代碼做解釋了,大家應(yīng)該都懂矛绘。
接下來就是在界面上顯示數(shù)據(jù)了:
<pre><code>
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! UITableViewCell
var fmt=NSDateFormatter()
fmt.dateFormat = "yyyy-MM-dd-hh-mm-ss"
let date = self.timearry.times[indexPath.row] as! Time
let showtime = fmt.stringFromDate(date.time)
cell.textLabel!.text = showtime
return cell
}
</code></pre>
"timearry"這個(gè)對(duì)象的times屬性就是我們要讀取的內(nèi)容耍休,你可以把他當(dāng)作一個(gè)數(shù)組來操作,因?yàn)樗部梢杂孟卤韥慝@取期中的每一個(gè)數(shù)據(jù)货矮。
若你在之前data model沒有勾選Ordered則在這里生成的times是NSSet類型羊精,那么就不可以用下標(biāo)來獲取內(nèi)容了。
現(xiàn)在來運(yùn)行下app:
現(xiàn)在來添加刪除功能,以下代碼:
<pre><code>
// Override to support conditional editing of the table view.
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
// Return NO if you do not want the specified item to be editable.
return true
}
// Override to support editing the table view.
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
//1
let timeToRemove = self.timearry.times[indexPath.row] as! Time
//2
let times = self.timearry.times.mutableCopy() as! NSMutableOrderedSet
times.removeObject(timeToRemove)
self.timearry.times = times.copy() as! NSOrderedSet
//3
managedContext.deleteObject(timeToRemove)
//4
var error: NSError?
if !managedContext.save(&error) {
println("Could not save: \(error)")
}
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
} else if editingStyle == .Insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
</code></pre>
第一個(gè)方法添加左滑編輯功能抓督。
來解釋下第二個(gè)方法燃少,當(dāng)你點(diǎn)擊Delete會(huì)調(diào)用此方法:
- 首先獲取要?jiǎng)h除的對(duì)象
- 先獲取NSOrderedSet對(duì)象
- 從中刪除對(duì)象
- 同步到self.timearry.times
- 從內(nèi)存中刪除對(duì)象
- 保存‘暫存器’
大功告成,這一篇寫的好艱苦啊铃在,邏輯混亂供汛,有看不懂的小朋友,實(shí)在不好意思了涌穆,不清楚的部分請(qǐng)留言怔昨,我來解釋。
最后運(yùn)行一下吧:
源代碼已上傳Github:https://github.com/superxlx/CoreDataTest3