ios 如何使用 Core Data 访问和使用实体
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/28916937/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I access and use an entity using Core Data
提问by slifty
It's time to use Core Data, but the open documentation and guides out there spend a lot of time talking about general setup and nitty gritty "behind the scenes" details. Those things are important, but I would love a quick, clean source showing how to actually use information stored in a Core Data model.
是时候使用Core Data 了,但是那里的开放文档和指南花费了大量时间讨论一般设置和“幕后”细节。这些事情很重要,但我希望有一个快速、干净的源代码来展示如何实际使用存储在 Core Data 模型中的信息。
The Scenario
情景
In my simple example I have a single entity type:
在我的简单示例中,我有一个实体类型:
Job
- salary [Double]
- dateCreated [Date]
This is a Swift iOS app powered by story boards, with the default generated AppDelegate.swift which handles the generation of my Managed Object Context.
这是一个由故事板提供支持的 Swift iOS 应用程序,默认生成的 AppDelegate.swift 处理我的托管对象上下文的生成。
The Question
问题
How do I use Job instances in my application?
如何在我的应用程序中使用 Job 实例?
Bonus points if you can also provide insight around these items:
- As someone who is used to the MVC design pattern, how do I avoid including dirty data access inside of my controllers without bucking iOS development best practices?
- How can I access entities from Core Data while following DRY?
- How do I pass managed objects between methods and controllers while maintaining their type?
如果您还可以提供有关这些项目的见解,则可以获得加分:
- 作为习惯了 MVC 设计模式的人,如何在不违背 iOS 开发最佳实践的情况下避免在控制器内部包含脏数据访问?
- 如何在遵循DRY 时从 Core Data 访问实体?
- 如何在保持其类型的同时在方法和控制器之间传递托管对象?
The Core Data documentation provides some snippetsfor fetching records. This question is essentially asking where that logic belongs in an iOS application, and how to actually interact with the fetched records after fetching them.
Core Data 文档提供了一些用于获取记录的片段。这个问题本质上是询问该逻辑在 iOS 应用程序中的位置,以及在获取记录后如何与获取的记录进行实际交互。
An Example
一个例子
This question isn't meant to be a broad, sweeping question so I will ground it in an example attempt to use Core Data. In my example I have a single UIViewController which has a label. I want this label to show the salary from a job.
这个问题并不是一个广泛而全面的问题,所以我将把它放在一个尝试使用 Core Data 的示例中。在我的示例中,我有一个带有标签的 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()
}
}
This example will not compile for two reasons:
这个例子不会编译有两个原因:
- I didn't specify a return type for getCurrentJob, since I wasn't sure what type to return
- The line with "ERRORS", which tries to access the
salary
attribute, will error because there is no way of knowing that salary is an actual attribute of job.
- 我没有为 getCurrentJob 指定返回类型,因为我不确定要返回什么类型
- 尝试访问
salary
属性的带有“ERRORS”的行会出错,因为无法知道薪水是工作的实际属性。
How do I pass around and use the job object?
如何传递和使用作业对象?
采纳答案by slifty
The key missing piece from the above example are NSManagedObject subclasses, and in Swift, the @NSManaged
Swift annotation. NSManagedObject is a generic class which, in its most simple form, can be extended to simply provide access to attributes of a Core Data entity, but in reality this is where traditional model logic should live.
上面示例中缺少的关键部分是NSManagedObject 子类,而在 Swift 中,则是@NSManaged
Swift 注释。NSManagedObject 是一个通用类,以其最简单的形式,可以扩展为简单地提供对核心数据实体的属性的访问,但实际上这是传统模型逻辑应该存在的地方。
Creating NSManagedObject Subclasses
创建 NSManagedObject 子类
You can automatically generate these objects by viewing the Core Data model, and using the menu command: Editor->Create NSManagedObject Subclass
.
您可以自动生成通过查看核心数据模型,并使用菜单命令这些对象:Editor->Create NSManagedObject Subclass
。
This will generate Job.swift
(or whatever your entity name is)
这将生成Job.swift
(或任何您的实体名称)
import Foundation
import CoreData
class Job: NSManagedObject {
@NSManaged var dateCreated: NSDate
@NSManaged var salary: NSNumber
}
Using NSManagedObject Subclasses
使用 NSManagedObject 子类
Your new class is now available for use, and you can typecast the fetched result accordingly! For completion, here's the updated version of the previously broken example
您的新类现在可以使用了,您可以相应地对获取的结果进行类型转换!为了完成,这是以前损坏的示例的更新版本
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()
}
}
回答by Drux
For instance, create a Core Data model like this:
例如,创建一个像这样的核心数据模型:
Now generate Swift source code from it (with Editor
| Create NSManagedObject Subclass
). This will allow you to compile the following version of JobViewController
(which currently lacks error handling and more):
现在从中生成 Swift 源代码(使用Editor
| Create NSManagedObject Subclass
)。这将允许您编译以下版本JobViewController
(目前缺少错误处理等):
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) $"
}
}
回答by kellanburket
Although I know that some hardcore OOP advocates will frown at this solution, I suggest using singleton classes to manage Core Data across an application.
虽然我知道一些铁杆 OOP 拥护者会对这个解决方案皱眉,但我建议使用单例类来管理跨应用程序的核心数据。
I'd advise setting up a global CoreDataManager that can be accessed through a shared instance. Now you have global access to your retrieval, update, deletion methods, etc, and your global variable is kept private.
我建议设置一个可以通过共享实例访问的全局 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
}
}
This can be initialized inside of your AppDelegate's didFinishingLaunchingWithOptions function
这可以在 AppDelegate 的 didFinishingLaunchingWithOptions 函数中初始化
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
CoreDataManager.initialize(self.managedObjectContext!)
return true
}
You can set up your NSManagedObject
by clicking on YourProject.xcdatamodeld
in the navigator. In your case, you'll add a Job
entity with attributes salary
(double) and date
(date). On the top menu access Editor > CreateNSManagedObjectSubclass to automatically generate your Job subclass. While you're still in the xcdatmodel editor, open up the rightmost pane--you should see text fields for 'Name' and 'Class'. Be sure to change your class to 'ProjectName.Name`--in your case 'ProjectName.Job'--or you won't be able to instantiate your new NSManagedObject class.
您可以NSManagedObject
通过单击YourProject.xcdatamodeld
导航器中的 进行设置。在您的情况下,您将添加一个Job
具有属性salary
(double) 和date
(date)的实体。在顶部菜单访问 Editor > CreateNSManagedObjectSubclass 以自动生成您的 Job 子类。当您仍在 xcdatmodel 编辑器中时,打开最右侧的窗格——您应该会看到“Name”和“Class”的文本字段。请务必将您的类更改为“ProjectName.Name”——在您的情况下为“ProjectName.Job”——否则您将无法实例化您的新 NSManagedObject 类。
Your NSManagedObject class should be automatically generated for you and available for inspection in the project navigator. It will look like this:
您的 NSManagedObject 类应该会自动为您生成,并且可以在项目导航器中进行检查。它看起来像这样:
import Foundation
import CoreData
@objc class Job: NSManagedObject {
@NSManaged var salary: NSNumber
@NSManaged var date: NSDate
}
In order to restrict access to your managed objects, you should create mediator classes with get- and set-style variable. Although Swift doesn't have a 'protected' access level, you can keep your NSManagedObjects private and allow access through the object Mediators by grouping them into one class file:
为了限制对托管对象的访问,您应该使用 get- 和 set-style 变量创建中介类。尽管 Swift 没有“受保护”访问级别,但您可以将 NSManagedObjects 保密,并通过将它们分组到一个类文件中来允许通过对象 Mediator 进行访问:
class ManagedObjectMediator<T: NSManagedObject> {
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<T: Job>: ManagedObjectMediator<Job> {
var date: NSDate {
return managedObject.date
}
var salary: NSNumber {
return managedObject.salary
}
init?(attributes: [String:AnyObject]) {
super.init("Job", attributes: attributes)
}
}