swiftM_iOSiOS过坑技巧

第三章 Core Data Stack

2016-01-01  本文已影响336人  smalldu

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

配图

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

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

这四个类,我们前面只遇到过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

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

只要你的数据类型是基于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的五个比较重要的特性:

创建自己的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的子类

创建出两个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)
            
            }
    }

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

上一篇 下一篇

猜你喜欢

热点阅读