iOS数据持久化iOS_Dev_Tipsios开发

大宝剑之CoreData(三)

2015-04-21  本文已影响10763人  StrongX

我把CoreData叫做大宝剑,为什么呢,因为CoreData用起来着实让人感到舒爽~~这一篇让我们来详细的了解一下大宝剑~

  • 参考书籍:CORE DATA by Tutorials

这一篇主要内容:


(1)、深入了解CoreData对象

之前在我们创建工程的时候勾选了Use CoreData的选择框,勾选这个选择框以后在AppDelegate.swift中自动生成了以下四个对象:

在这一篇中我们不勾选这个选择框,自己添加这四个类以便于详细学习“大宝剑”。像这样:


不勾选

NSManagedObjectModel是什么?
这个对象就是你的data model,代表了里面的每一个对象类型。
你可以把他当作是你的数据库的图形化显示。呼呼~~和“度娘”的解释差不多。

Note:如何将该对象与data model联系起来?
来看这个方法:

        let modelURL = NSBundle.mainBundle().URLForResource("CoreDataTest3", withExtension: "momd")!

通过这个方法,编译器将data model文件编译以后放入了一个.momd文件夹,并返回了这个文件夹的地址,通过这个地址我们初始化了我们的NSManagedObjectModel对象。


NSPersistentStore是什么?
无论你决定使用哪种方法来进行存储你都得使用NSPersistentStore来进行存储或者读写数据。CoreData提供了三种原子操作对象和一种非原子操作对象。
原子操作对象在你操作任何读写操作之前将数据全部读写到内存中,非原子操作对象则相反,在需要的时候加载数据。

来看看这四种NSPersistentStore对象


NSPersistentStoreCoordinator是什么?
这是NSManagedObjectModel和NSPersistentStore之间的桥梁。他能够向NSManagedObjectModel发送信息并存储,也能够从NSPersistentStore中读取信息。


NSManagedObjectContext是什么?
这是我们之前唯一见到过的一个类型了。我们已经提到过这些特性(更多特性后面的篇章会提到):


以上内容不了解也没事,因为我们会一个一个用到的。

(2)、自己来创建一个栈

创建一个新文件CoreDataStack.swift(在这个文件中,我们将自己添加全部之前xcode自动生成的代码,以便于对那四个对象的理解),添加以下代码:
<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>

一下子添加了这么一大块代码,一眼看过去一定是晕晕的,好的,一句一句来解释一下吧。

以上添加的代码仔细观察的话,很容易就会发现基本就是在我们勾选了 use core data 以后xcode自动生成的代码,而我们只是将它写在了一个类中,以便于观察及理解。


像我们上面那样创建了一个类,但是这个类根本没有参与到我们的程序中来,接下来就是将这个类参与到程序中。
打开AppDelegate.swift,添加以下代码:

         import CoreData

          lazy var coreDataStack = CoreDataStack()

如此一来coreDataStack这个变量就拥有了我们之前写的四个对象了。
在下面两个方法中添加coreDataStack.saveContext():

<pre><code>
func applicationDidEnterBackground(application: UIApplication) {
coreDataStack.saveContext()
}
func applicationWillTerminate(application: UIApplication) {
coreDataStack.saveContext()
}
</code></pre>

这俩方法确定应用在发生意外退出的时候保存数据,使数据不会丢失。


再来观察一下我们的文件目录,哦~我们还差一个.xcdatamodeld文件,那么就来新建一个吧。
右击文件目录->New file...->选择iOS Core Data->选择Data Model->next->命名为‘CoreDataTest3’,像这样:

1.png

现在来看我们的程序就和勾选了use core data选择一模一样了,接下来就可以进行前面两篇的操作,而且效果相同。哦~唯一的区别就是使用coreDataStack.context来替代前面篇章中AppDelegate.swift中的managedObjectContext。


(3)、relationships属性和删除功能

接下来的内容,我们通过一个Demo,来演示一下data model中的relationships属性和删除功能。
打开storyboard删除原有的控制器,拖入一个tableviewcontroller,并将之设置为程序人口,如图所示:

勾选红色框内的选择框

再为这个控制器创建一个类,如以下步骤:
New File->选择iOS Source,继续选择cocoa touch class->next->将其选择为UITableviewController的子类->next->Create

3.jpeg

同时将控制器和我们新建的文件进行绑定。


4.png

将控制器转化为Navigationcontroller,日图操作:

先选中控制器

给navigation Bar添加一个right Button,并添加动作‘addTime’,添加代码。
将实现以下功能,由于这段实现并不复杂,在这里就不进行描述,有问题的可以留言。

点击“+”按钮,在下面列表中添加时间

目前这个版本并没有实现数据持久化,当我们退出应用以后数据将消失,接下来我们做的就是将数据持久化,这听起来好像在前两篇我们已经做过了,不然~~~在这里我们将引进relationships的概念,同时实现删除功能。


这个逻辑是这样的,我们需要一个数组来存放时间,我们暂且把这个数组叫做‘时间组’,这个‘时间组’存放了许多时间,那么这个时间组就是一个Entity,里面存放的时间也是Entity,不过时间组这个Entity的每一个对象都拥有很多的时间Entity,先来创建这两个Entity,一个起名为“TimeArry”,一个起名为“Time”,在"Time"这个Entity中有一个属性time类型选择为NSDate:

1.png

而在“TimeArry”中则存在一个relationship,起名为“times”,指向“Time”Entity:

Note:relationship生成的属性是什么类型的?“To Many”生成的是NSSet类型,如果想使用下标来来使用对象的话,请在右边的属性栏中勾选Ordered选项,当你勾选了这个选项以后生成的“To Many”类型就是NSOrderedSet类型。
在这里我们使用NSOrderedSet类型

红色框内为Relationship属性

同时将Type选择为“To Many”,勾选Ordered

勾选Ordered
给我们的两个Entity生成对象类吧,生成方法在上一篇中已经讲过:
Editor—>Create NSManagedObject Subclass .............

首先要添加的代码当然是addTime方法来添加数据,在此方法中添加以下代码:
<pre><code>
@IBAction func addTime(sender: AnyObject) {
//获取当前时间
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>

代码解释:

Note:timearry就是我们在界面上显示的“TimeArry”对象的一个实例,按理来说TimeArry有很多个对象,每一个“TimeArry”对象都有自己的“Time”,这样说起来好像很像一个二维数组,而事实上我们只显示了这个二维数组的第一行。那么在进入程序的时候我们就得创建这个timearry对象。

添加一下代码:
<pre><code>

var timearry:TimeArry!
override func viewDidLoad() {
super.viewDidLoad()
//初始化暂存器
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
managedContext = appDelegate.coreDataStack.context
//1 获取“TimeArry”对象,并初始化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>

这段代码很容易理解(如果你看了我前面的两篇内容的话),就是读取了“TimeArry”的内容,然后如果存在数据,则使timearry为第一组数据,若不存在数据则初始化。

因为我们是用一个tableview来显示数据,所以添加以下代码:
<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>
就不对这段代码做解释了,大家应该都懂。

接下来就是在界面上显示数据了:
<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"这个对象的times属性就是我们要读取的内容,你可以把他当作一个数组来操作,因为他也可以用下表来获取期中的每一个数据。
若你在之前data model没有勾选Ordered则在这里生成的times是NSSet类型,那么就不可以用下标来获取内容了。

现在来运行下app:

退出app,重新登录数据还在,说明保存数据成功

现在来添加删除功能,以下代码:
<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>

第一个方法添加左滑编辑功能。
来解释下第二个方法,当你点击Delete会调用此方法:

大功告成,这一篇写的好艰苦啊,逻辑混乱,有看不懂的小朋友,实在不好意思了,不清楚的部分请留言,我来解释。
最后运行一下吧:

大功告成

源代码已上传Github:https://github.com/superxlx/CoreDataTest3

上一篇下一篇

猜你喜欢

热点阅读