第三章 Core Data Stack

注:本文来自Core Data by tutorials 2.0 , swift + iOS 9 .本文非翻译 只讲大体思路

配图

到目前为止,我们一直在依赖xcode自动创建的coredata模板,这没什么问题,但是如果真的想知道CoreData是怎么工作的,我们需要自己写代码创建对象

这里会涉及到四个Core Data的类:

  • NSManagedObjectModel
  • NSPersistentStore
  • NSPersistentStoreCoordinator
  • NSManagedObjectContext

这四个类,我们前面只遇到过NSManagedObjectContext

本小结将会学习这四个类的细节

The managed object model--NSManagedObjectModel

NSManagedObjectModel 代表你app中的每个数据对象类型,以及他们的属性和他们之间的关系。 上一章我们已经使用他创建对象保存属性和数据。

我们应该把NSManagedObjectModel当做一个数据模型,NSManagedObjectModel底层使用的是sqllite

sqllite只是CoreData可用的持久化类型之一,后面会详细介绍

我们用Xcode提供的visual editor创建/编辑了一个xcdatamodel file,然而真正在幕后的是一个叫momc的编译器(compiler),把model file编译后的结果放到momd文件夹下。Core Data可以很高效地在运行时使用momd文件夹里编译过的内容,来初始化一个NSManagedObjectModel实例。

持久化存储

Core Data提供了四种开箱即用的NSPersistentStore存储类型,三种原子型atomic的,一种非原子型non-atomic

一个原子性存储在你进行任何读写操作之前需要完全的反序列化比不过且加载到内存中,相反一个非原子性的存储可以在需要的时候一块块的把自己加载到内存。

  • NSQLiteStoreType 依托SQLite数据库,也是唯一的非原子型的non-atomic
  • NSXMLStoreType 依托于XML文件,是原子型的atomic NSXMLStoreType只在os x可用
  • NSBinaryStoreType 依托于二进制文件,是原子型的atomic
  • NSInMemoryStoreType 其实是存在于内存中persistent store type,不算严格意义上的持久化存储,通常用来做单元测试和缓存。

只要你的数据类型是基于JSON和CSV的格式,你还可以通过创建NSIncrementalStore的子类来创建自己的persistent store类型。

The persistent store coordinator

NSPersistentStoreCoordinatorNSManagedObjectModelNSPersistentStore的桥梁。他负责理解model,更好地来处理信息的存取。特别是有多个persistent stores时,persistent store coordinator相对managed context提供唯一的接口,保证了context与特定的persistent store交互。

The managed object context

之前的章节提到过context可以看做是内存中的暂存器,来记录所有的managed object的行为,当然managed object所做的任何改变,在context没有save()之前,都是不会在数据库中生效的。

关于context的五个比较重要的特性:

  • The context 管理着对象们的生命周期,不管这些对象是create还是fetch到的,这种对生命周期的管理在faulting、inverse、relationship handling 和 validation时很有用。

  • A managed object不能脱离context而存在,他们是紧紧地绑在一起的。

  • contexts都很有领土意识,一旦一个managed object被归到某个context中去了,那么这个managed object在他整个生命周期内属于这个context了。

  • 一个应用可以使用多个context,少数的Core Data应用都这么搞。

  • 一个 context是非线程安全的,你最好不要跨线程去使用context,Apple提供了多种方式来在多线程中使用context,后面会讲到。

创建自己的stack对象

作者带我们创建了一个CoreDataStack对象

完整版如下:

import CoreData

class CoreDataStack{
    
    let modelName = "Dog Walk"
    private lazy var applicationDocumentDirectiry:NSURL = {
        let urls = NSFileManager.defaultManager().URLsForDirectory(
            .DocumentDirectory, inDomains: .UserDomainMask)

        return urls[urls.count-1]
    }()
    
    lazy var context: NSManagedObjectContext = {
        var managedObjectContext = NSManagedObjectContext(
        concurrencyType: .MainQueueConcurrencyType)
        managedObjectContext.persistentStoreCoordinator = self.psc
        return managedObjectContext
    }()
    
    private lazy var psc: NSPersistentStoreCoordinator = {
        let coordinator = NSPersistentStoreCoordinator(
        managedObjectModel: self.managedObjectModel)
        let url = self.applicationDocumentDirectiry.URLByAppendingPathComponent(self.modelName)
        
        do {
            let options = [NSMigratePersistentStoresAutomaticallyOption : true]
            try coordinator.addPersistentStoreWithType(
            NSSQLiteStoreType, configuration: nil, URL: url,
            options: options)
        } catch {
            print("Error adding persistent store.")
        }
        return coordinator
    }()
    
    private lazy var managedObjectModel: NSManagedObjectModel = {
            let modelURL = NSBundle.mainBundle()
            .URLForResource(self.modelName,
            withExtension: "momd")!
            return NSManagedObjectModel(contentsOfURL: modelURL)!
    }()
    
    
    func saveContext(){
    
        if self.context.hasChanges{
            do {
                try self.context.save()
            } catch let error as NSError {
                print("Error: \(error.localizedDescription)")
                abort()
            }
            
        }
    }
    
}

简单的看其实就是这样

class CoreDataStack 
{ 
  let context:NSManagedObjectContext 
  let psc:NSPersistentStoreCoordinator 
  let model:NSManagedObjectModel 
  let store:NSPersistentStore?
  let modelName:String

  func saveContext(){}
}

上文提到的四个属性掌握好就行了,下面解释下

首先导入CoreData库,加入modelName,(因为我们的项目并没有勾选use core data,管理对象模型需要我们自己创建,这个name就是对应的那个)。

applicationDocumentsDirectory 返回我们app,document目录的一个懒加载属性。

其他四个每个类型都对应一个Core Data stack的组成部分。他们之间互相依赖并且都使用了懒加载

唯一外面可用的属性就是NSManagedObjectContext

最后提供了保存的快捷方法

然后转到ViewController.swift ,导入CoreData
声明一个var managedContext: NSManagedObjectContext!

打开AppDelegate.swift
1、import CoreData
2、lazy var coreDataStack = CoreDataStack()
3、

let navigationController =
window!.rootViewController as! UINavigationController
let viewController =
navigationController.topViewController as! ViewController
viewController.managedContext = coreDataStack.context

4、

    //确保app在进入后台 或者 终端前 能保存待定数据
    func applicationDidEnterBackground(application: UIApplication) {
        coreDataStack.saveContext()
    }
    
    func applicationWillTerminate(application: UIApplication) {
        coreDataStack.saveContext()
    }
Modeling your data

我们这次是没有.xcdatamodel文件的 需要自己创建

1、File\New\File…
2、iOS\Core Data\Data Model
3、Next
4、命名Dog Walk,(对应之前的modelName)
5、Create

插图

根据下图 创建一个Entity


插图

然后创建第二个Entity

插图

这里Dog和Walk是一对多的关系(用过数据库都知道)

所以这里要添加一个relationships
在Dog中添加一个walks

插图

在旁边的属性查看器中修改,type为one to many,勾选order

插图

在Walk中也建立一个relationship

插图

切换下Editor Style,(右下角)

插图

这个图清楚的表现出他们之间的关系

添加managed object的子类
  • Editor\Create NSManagedObject Subclass…
  • Dog Walk
  • 勾选Dog 和 Walk

创建出两个NSManagedObject的子类,就像第二章学过的那样

import Foundation
import CoreData
extension Dog {
@NSManaged var name: String?
@NSManaged var walks: NSOrderedSet?
}
import Foundation
import CoreData
extension Walk {
@NSManaged var date: NSDate?
@NSManaged var dog: Dog?
}

然后 回到ViewController中声明var currentDog: Dog!

然后 在viewDidLoad中加上以下代码

 let dogEntity = NSEntityDescription.entityForName("Dog", inManagedObjectContext: managedContext)
    let dogName = "laal"
    let dogFetch = NSFetchRequest(entityName: "Dog")
    dogFetch.predicate = NSPredicate(format: "name == %@",dogName)
    do {
       let result = try managedContext.executeFetchRequest(dogFetch) as! [Dog]
        if result.count>0{
            currentDog = result.first
       }else{
        currentDog = Dog(entity: dogEntity!,
        insertIntoManagedObjectContext: managedContext)
        currentDog.name = dogName
        try managedContext.save()
       }
    } catch let error as NSError {
            print("Error: \(error.localizedDescription)")
    }

以上代码取出所有名字为"laal"的Dog如果没有 就创建一个加到数据库中 然后 保存

然后就是替换tableview的一些操作

  func tableView(tableView: UITableView,
    numberOfRowsInSection section: Int) -> Int {
      
      return currentDog.walks!.count
  }
  func tableView(tableView: UITableView,
    cellForRowAtIndexPath
    indexPath: NSIndexPath) -> UITableViewCell {
      
      let cell =
      tableView.dequeueReusableCellWithIdentifier("Cell",
        forIndexPath: indexPath) as UITableViewCell
      
      let walk = currentDog.walks![indexPath.row] as! Walk
      cell.textLabel!.text = dateFormatter.stringFromDate(walk.date!)
      
      return cell
  }

add中 修改如下

@IBAction func add(sender: AnyObject) {
        let walkEntity = NSEntityDescription.entityForName("Walk",
        inManagedObjectContext: managedContext)
        let walk = Walk(entity: walkEntity!,
        insertIntoManagedObjectContext: managedContext)
        walk.date = NSDate()
        //Insert the new Walk into the Dog's walks set
        let walks = currentDog.walks!.mutableCopy() as! NSMutableOrderedSet
        walks.addObject(walk)
        currentDog.walks = walks.copy() as? NSOrderedSet
        do {
            try self.managedContext.save()
        } catch let error as NSError {
            print("Error: \(error.localizedDescription)")
        }
        tableView.reloadData()
  }

作者还添加了删除功能

 func tableView(tableView: UITableView,
            canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
            return true
    }
    
    func tableView(tableView: UITableView,
            commitEditingStyle
            editingStyle: UITableViewCellEditingStyle,
            forRowAtIndexPath indexPath: NSIndexPath) {
           
            if editingStyle == UITableViewCellEditingStyle.Delete {
                let walkToRemove = currentDog.walks![indexPath.row] as! Walk
            
                managedContext.deleteObject(walkToRemove)
            do {
                try managedContext.save()
            } catch let error as NSError {
                print("Error: \(error.localizedDescription)")
            }
            
            //4
            tableView.deleteRowsAtIndexPaths([indexPath],
        withRowAnimation: UITableViewRowAnimation.Automatic)
            
            }
    }

这些代码很容易理解就不多赘述了

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容

  • 适读对象: 需要入门Core Data的朋友; 像我一样,尚未学过数据库相关课程,不太懂怎么写SQLite语句的朋...
    AntonyWong阅读 5,036评论 8 21
  • 虽然从接触iOS开发开始,做的每一个项目都在用Core Data,但是一些比较底层的东西都是boss写的或者用的是...
    卖萌凉阅读 2,797评论 1 11
  • 1. Managed object model 它代表数据模型,以及它们的属性,它们之间的关系。Core Data...
    JaiUnChat阅读 292评论 0 0
  • 转载自Core Data概述 Core Data 可能是 OS X 和 iOS 里面最容易被误解的框架之一,为了帮...
    夏天的风_song阅读 865评论 0 4
  • 来上海第一次去外滩。 黄昏里的东方明珠 去朋友的公司玩,随手拍 国庆和大学室友到中华艺术宫玩。
    夏虫非语冰阅读 290评论 0 3