如何使用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() } }
此示例无法编译,原因有两个:
- 我没有为getCurrentJob指定返回类型,因为我不确定要返回什么类型
- 试图访问
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) } }