CoreData: 第一个CoreData应用

在开发中使用过几次CoreData,还算是比较熟悉了,觉得有必要整理下。CoreData是非常重要的一个框架,功能丰富,想要完全的掌握还需要花时间。

该系列主要参考《Core Data by Tutorials: iOS 8 and Swift Edition》,推荐购买正版看英文原版。

CoreData是什么就不讲解了,最开始接触CoreData的时候感觉像个ORM框架,但是并没有这么简单。搜索自行了解CoreData是什么东西,下面我们就直接开始了。

开始

首先我们先做个一个App,这个App很简单,用来显示我们的仓库的列表,我们可以添加仓库。

我们新建一个项目,如下所示,记得勾选Use Core Data,如下所示:

alt text

新建完成后你可以看见比我们平时不使用Core Data的项目要多了一些东西,而且AppDelegate.swift里面也多了不少的代码。这些东西都是Xcode的模板为我们自动生成了,这些配置对于大多数的App都够用了,后面我们会自己创建Core Data Stack,不使用Xcode为我们创建的。

先不讲解CoreData的这些东西,我们先完成这个简单的App。

View Controller包含在Navigation Controller里面,如下所示:

alt text

View Controller添加如下所示的内容:

alt text

请自行给Tableview设置约束。然后设置Tableview的dataSource,如下所示:

alt text

这里只需要设置dataSource即可,然后给Tableview设置@IBOutlet,以及设置Bar Button Item的selector,如下所示

alt text

alt text

View Controller的代码如下:

import UIKit

class ViewController: UIViewController, UITableViewDataSource {

    @IBOutlet weak var tableView: UITableView!

    var warehouses = [String]()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        title = "仓库管理"
        tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    @IBAction func add(sender: AnyObject) {
        var alert = UIAlertController(title: "添加仓库", message: "输入仓库名", preferredStyle: .Alert)
        let saveAction = UIAlertAction(title: "保存", style: .Default) { (action: UIAlertAction!) -> Void in
            let textField = alert.textFields![0] as! UITextField
            self.warehouses.append(textField.text)
            self.tableView.reloadData()
        }
        let cancelAction = UIAlertAction(title: "取消", style: .Default) {
                (action: UIAlertAction!) -> Void in
        }

        alert.addTextFieldWithConfigurationHandler {
                (textField: UITextField!) -> Void in
        }

        alert.addAction(saveAction)
        alert.addAction(cancelAction)

        presentViewController(alert, animated: true, completion: nil)
    }

    // MARK: UITableViewDataSource
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return warehouses.count
    }
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! UITableViewCell
        cell.textLabel!.text = warehouses[indexPath.row]
        return cell
    }

}

代码很简单,就不一一讲解了。

运行后模拟器截图如下:

alt text

点击添加可以添加新的仓库,但是这些数据并没有被保存起来,在程序运行期间存在于内存中。当我们重启设备或者退出(注意这里的退出不是指点击home键退出到主屏幕,而是从后台中清除),这些数据就不会存在了。所以我们就需要CoreData来持久化数据,当然你也可以用其他的的持久化手段,比如使用SQLite或者读写文件等等。接下来我们就使用CoreData来持久化这些数据。

CoreData

以前做Web开发的时候,使用过ORM映射框架,比如NHibernate,第一步就是映射模型。

模型化数据

所以我们先要创建managed object model,Xcode自动为我们生成了数据模型文件,叫做WarehouseList.xcdatamodeld

alt text

点击Add Entity,命名为Warehouse,如上所示。

  • 一个entity就是就是CoreData里面定义的类。典型的例子就是员工和公司。在关系型数据库中,一个entity就对应一张表。
  • 一个attribute是与entity相关的部分信息。比如,员工entity就可能有员工名字,位置,薪水等attribute。在数据库中,一个attribute就对应表中的列。
  • relationship是多个entity之间的联系。在CoreData中,两个entity之间可以有to-one relationshipsto-many relationships关系

现在我们给Warehouse添加一个叫做nameAttributes,选择String类型,如下所示。

alt text

保存

使用CoreData需要引入CoreData框架,在ViewController.swift中添加如下代码:

import CoreData

修改如下代码:

var warehouses = [NSManagedObject]()

我们需要存储Warehouse的实体,所以我们需要修改为如上所示的代码。然后修改UITableViewDataSource相关的方法,如下所示:

// MARK: UITableViewDataSource
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return warehouses.count
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! UITableViewCell
        let warehouse = warehouses[indexPath.row]
        cell.textLabel!.text = warehouse.valueForKey("name") as? String
        return cell
    }

接下来我们就要修改添加仓库的方法,让CoreData将添加的数据持久化。修改add方法中的saveAction,如下所示:

let saveAction = UIAlertAction(title: "保存", style: .Default) {
    (action: UIAlertAction!) -> Void in
    let textField = alert.textFields![0] as! UITextField
    self.saveName(textField.text)
    self.tableView.reloadData()
}

saveName为保存操作,我们将单独提出来:

func saveName(name: String) {
    //1
    let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
    let managedContext = appDelegate.managedObjectContext!

    //2
    let entity = NSEntityDescription.entityForName("Warehouse", inManagedObjectContext: managedContext)
    let warehouse = NSManagedObject(entity: entity!, insertIntoManagedObjectContext: managedContext)
    //3
    warehouse.setValue(name, forKey: "name")
    //4
    var error: NSError?
    managedContext.save(&error)
    if error != nil {
        println("Could not save \(error), \(error!.userInfo)") }
    //5
    warehouses.append(warehouse)
}
  1. 在你能够从CoreData存储里面保存或者查询很合数据之前,你首先需要一个NSManagedObjectContext,你可以将managed object context想象成内存中的一个暂存器。保存一个新的managed object到CoreData中需要两步:第一,你将一个新的managed object插入到managed object context;然后,提交这些改变到managed object context保存到硬盘。因为我们使用了模板,所以我们使用app delegate里面的NSManagedObjectContext。
  2. 我们创建了一个新的managed object然后插入到了managed object context中。你可能会奇怪NSEntityDescription是什么,NSManagedObject就像一个变形者,能够代表任何的entity。NSEntityDescription能够在运行时连接你定义在数据模型中实体的实例。
  3. 然后我们设置实体的属性。
  4. 最后调用managedContext的save方法来保存数据,如果有错误则打印出来。
  5. 最后将新数据添加到数据源中。

然后我们运行添加一个仓库,如下所示:

alt text

但是当我们重启的时候会发现列表是空的,就不截图了,主要原因是因为我们并没有从CoreData中查询数据,所以,接下来我们就来查询数据。

查询

我们在ViewController.swift中添加如下代码:

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)

    //1
    let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
    let managedContext = appDelegate.managedObjectContext!

    //2
    let fetchRequest = NSFetchRequest(entityName: "Warehouse")

    //3
    var error: NSError?

    let fetchedResults = managedContext.executeFetchRequest(fetchRequest, error: &error) as? [NSManagedObject]

    if let results = fetchedResults {
        warehouses = results
    } else {
        println("Could not fetch \(error), \(error!.userInfo)")
    }
}
  1. 我们仍然需要managed object context,查询也不列外。
  2. 顾名思义NSFetchRequest负责从CoreData中查询数据。功能强大且很灵活,你可以查询一些符合特定条件的数据集合(比如查询居住在威斯康辛州并且工龄至少3年的所有员工)等等。
  3. 然后managed object context执行executeFetchRequest(_:error:)方法,返回了一个可选的数组。

注意:如果没有符合查询条件的数据,那么该方法就会返回一个没有数据的空的可选数组。如果发生了错误,这个方法就会返回nil。

现在让我们运行,截图如下所示,列表中就有了我之前添加的数据了:

alt text

程序完整源码

坚持原创技术分享,您的支持将鼓励我继续创作!