如何使用Core Data访问和使用实体

现在是时候使用Core Data了 ,但开放的文档和指南花了很多时间来讨论一般设置和细节“幕后”细节。 这些事情很重要,但我希望快速,干净的资料显示如何实际使用存储在Core Data模型中的信息。

情景

在我的简单示例中,我有一个实体类型:

Job - salary [Double] - dateCreated [Date] 

这是一个由故事板驱动的Swift iOS应用程序,默认生成的AppDelegate.swift处理我的托管对象上下文的生成。

问题

如何在我的应用程序中使用Job实例?

如果您还可以提供有关这些项目的见解,那么奖励积分:

  • 作为习惯于MVC设计模式的人,如何在不违反iOS开发最佳实践的情况下避免在我的控制器中包含脏数据访问?
  • 如何在关注DRY时从Core Data访问实体?
  • 如何在保持类型的同时在方法和控制器之间传递托管对象?

Core Data文档提供了一些用于获取记录的片段 。 这个问题基本上是询问逻辑在iOS应用程序中的位置,以及如何在获取它们之后实际与获取的记录进行交互。

一个例子

这个问题并不是一个广泛而彻底的问题,因此我将在尝试使用核心数据的基础上进行研究。 在我的例子中,我有一个带有标签的UIViewController。 我希望这个标签显示工作的薪水。

 import UIKit import CoreData class JobViewController: UIViewController { @IBOutlet var salaryLabel: UILabel! let managedObjectContext = (UIApplication.sharedApplication().delegate as AppDelegate).managedObjectContext func updateLabel() { var job = getCurrentJob() salaryLabel.text = job.salary // ERRORS } func getCurrentJob()->(???) { var error: NSError? if let fetchedResults = managedObjectContext!.executeFetchRequest(NSFetchRequest(entityName:"Job"), error: &error) { return fetchedResults[0] } } override func viewDidLoad() { super.viewDidLoad() } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } } 

此示例无法编译,原因有两个:

  1. 我没有为getCurrentJob指定返回类型,因为我不确定要返回什么类型
  2. 试图访问salary属性的“ERRORS”行会出错,因为无法知道薪水是作业的实际属性。

如何传递并使用作业对象?

上面示例中的关键缺失部分是NSManagedObject子类 ,而在Swift中,是@NSManaged Swift注释。 NSManagedObject是一个generics类,它以最简单的forms可以扩展为只提供对Core Data实体属性的访问,但实际上这是传统模型逻辑应该存在的地方。

创建NSManagedObject子类

您可以通过查看Core Data模型并使用菜单命令自动生成这些对象: Editor->Create NSManagedObject Subclass

这将生成Job.swift (或任何您的实体名称)

 import Foundation import CoreData class Job: NSManagedObject { @NSManaged var dateCreated: NSDate @NSManaged var salary: NSNumber } 

使用NSManagedObject子类

您的新课程现在可供使用,您可以相应地对获取的结果进行类型转换! 完成后,这是以前破坏的示例的更新版本

 import UIKit import CoreData class JobViewController: UIViewController { @IBOutlet var salaryLabel: UILabel! let managedObjectContext = (UIApplication.sharedApplication().delegate as AppDelegate).managedObjectContext func updateLabel() { var job:Job = getCurrentJob() salaryLabel.text = job.salary // ERRORS } func getCurrentJob()->Job { var error: NSError? if let fetchedResults = managedObjectContext!.executeFetchRequest(NSFetchRequest(entityName:"Job"), error: &error) { return fetchedResults[0] } } override func viewDidLoad() { super.viewDidLoad() } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } } 

例如,创建一个这样的Core Data模型:

在此处输入图像描述

现在从它生成Swift源代码(使用Editor | Create NSManagedObject Subclass )。 这将允许您编译以下版本的JobViewController (目前缺少error handling等):

 import UIKit import CoreData class JobViewController: UIViewController { @IBOutlet var salaryLabel: UILabel! let managedObjectContext = (UIApplication.sharedApplication().delegate as AppDelegate).managedObjectContext var jobs: [Job] = [] override func viewDidLoad() { super.viewDidLoad(); jobs = managedObjectContext.executeFetchRequest(NSFetchRequest(entityName:"Job"), error: nil) as [Job]; } func updateLabel() { salaryLabel.text = "\(jobs[0].salary) $" } } 

虽然我知道一些硬核OOP倡导者会对这个解决方案不满,但我建议使用单例类来管理整个应用程序的核心数据。

我建议设置一个可以通过共享实例访问的全局CoreDataManager。 现在,您可以全局访问检索,更新,删除方法等,并且您的全局变量将保持私有状态。

 private var sharedCoreDataManager: CoreDataManager! class CoreDataManager { let managedContext: NSManagedObjectContext class var shared: CoreDataManager { return sharedCoreDataManager } class func initialize(context: NSManagedObjectContext) { sharedCoreDataManager = CoreDataManager(context: context) } private init(context: NSManagedObjectContext) { managedContext = context } func delete(entity: String, index: Int) -> Bool { var data = fetch(entity) if data != nil { managedContext.deleteObject(data![index]) data!.removeAtIndex(index) managedContext.save(nil) return true } return false } func fetch(entity: String) -> [NSManagedObject]? { var request = NSFetchRequest(entityName: entity) var error: NSError? if let entities = managedContext.executeFetchRequest(request, error: &error) as? [NSManagedObject] { if entities.count > 0 { return entities } } return nil } func save(entity: String, _ attributes: [String: AnyObject]) -> NSManagedObject? { var entity = NSEntityDescription.entityForName(entity, inManagedObjectContext: managedContext) let object = NSManagedObject(entity: entity!, insertIntoManagedObjectContext: self.managedContext) for (key, attr) in attributes { object.setValue(attr, forKey: key) } var error: NSError? if !managedContext.save(&error) { return nil } return object } } 

这可以在AppDelegate的didFinishingLaunchingWithOptions函数中初始化

 func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { CoreDataManager.initialize(self.managedObjectContext!) return true } 

您可以通过单击导航器中的YourProject.xcdatamodeld来设置NSManagedObject 。 在您的情况下,您将添加具有属性salary (double)和date (date)的Job实体。 在顶部菜单中,访问Editor> CreateNSManagedObjectSubclass以自动生成Job子类。 当你仍然在xcdatmodel编辑器中时,打开最右边的窗格 – 你应该看到’Name’和’Class’的文本字段。 请确保将您的类更改为“ProjectName.Name” – 在您的“ProjectName.Job”中 – 或者您将无法实例化新的NSManagedObject类。

应自动为您生成NSManagedObject类,并在项目导航器中进行检查。 它看起来像这样:

 import Foundation import CoreData @objc class Job: NSManagedObject { @NSManaged var salary: NSNumber @NSManaged var date: NSDate } 

为了限制对托管对象的访问,您应该使用get-和set-style变量创建中介类。 虽然Swift没有“受保护”的访问级别,但您可以将NSManagedObjects保密,并允许通过对象Mediators进行访问,方法是将它们分组到一个类文件中:

 class ManagedObjectMediator { private var managedObject: T! init?(_ type: String, attributes: [String: AnyObject]) { if let newManagedObject = CoreDataManager.shared.save(type, attributes) { managedObject = newManagedObject as T } else { return nil } } } class JobMediator: ManagedObjectMediator { var date: NSDate { return managedObject.date } var salary: NSNumber { return managedObject.salary } init?(attributes: [String:AnyObject]) { super.init("Job", attributes: attributes) } }