【发布时间】:2015-05-06 21:38:38
【问题描述】:
在进入我的问题之前,请先看看这张图片。
这是实际的数据模型:
我从 Web API 检索一组记录,从中创建对象,将它们保存在核心数据中并在“今天”视图中显示它们。默认情况下,返回当前日期的这些记录。
用户可以点击过去按钮转到单独的视图,在该视图中他可以从日期选择器视图中选择过去或未来的日期,并查看该选定日期的记录。这意味着我必须再次调用 API 传递选定的日期,检索数据并将该数据保存在核心数据中并显示它们。当用户离开这个视图时,这些数据应该被丢弃。
这是重要的部分。即使我获得了一组新数据,“今天”视图中当前日期的旧原始数据也不能消失。因此,如果/当用户返回 Today 视图时,该数据应该在他离开时随时可用,而无需应用调用 API 并再次获取当前日期的数据。
我想创建一个单独的NSManagedObjectContext 来保存这些临时数据。
我有一个名为DatabaseManager 的单独类来处理与核心数据相关的任务。此类使用 `NSManagedObjectContext 的实例进行初始化。它在给定的上下文中创建托管对象类。
import CoreData
import Foundation
import MagicalRecord
import SwiftyJSON
public class DatabaseManager {
private let context: NSManagedObjectContext!
init(context: NSManagedObjectContext) {
self.context = context
}
public func insertRecords(data: AnyObject, success: () -> Void, failure: (error: NSError?) -> Void) {
let json = JSON(data)
if let records = json.array {
for recordObj in records {
let record = Record.MR_createInContext(context) as Record
record.id = recordObj["Id"].int
record.name = recordObj["Name"].string!
record.date = NSDate(string: recordObj["Date"].string!)
}
context.MR_saveToPersistentStoreAndWait()
success()
}
}
}
所以在 Today 视图中,我将 NSManagedObjectContext.MR_defaultContext() 传递给 insertRecords() 方法。我还有一种方法可以从给定的上下文中获取记录。
func fetchRecords(context: NSManagedObjectContext) -> [Record]? {
return Record.MR_findAllSortedBy("name", ascending: true, inContext: context) as? [Record]
}
从 API 中检索数据,保存在核心数据中并成功显示。到目前为止一切顺利。
在过去的观点中,我必须做基本相同的事情。但是因为我不想改变原始数据。我尝试了 MagicalRecord 提供的几种方法。
尝试 #1 - NSManagedObjectContext.MR_context()
我用NSManagedObjectContext.MR_context() 创建了一个新的上下文。我在过去视图中更改了日期,该选定日期的数据被成功检索并保存在数据库中。但这是问题所在。当我从核心数据中获取对象时,我也得到了旧数据。例如,每天只有 10 条记录。在今天视图中,我显示 10 条记录。当在过去视图中获取对象时,我得到 20 个对象!我假设它是旧的 10 个对象加上新的对象。此外,当我尝试在 tableview 中显示它们时,它会因cellForRowAtIndexPath 方法中的 EXC_BAD_ACCESS 错误而崩溃。
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
let record = records[indexPath.row]
cell.textLabel?.text = record.name // EXC_BAD_ACCESS
cell.detailTextLabel?.text = record.date.toString()
return cell
}
尝试 #2 - NSManagedObjectContext.MR_newMainQueueContext()
当我更改日期并出现以下错误时,应用程序崩溃。
'+entityForName: nil 不是合法的 NSPersistentStoreCoordinator 用于搜索实体名称'Record''
尝试 #3 - NSManagedObjectContext.MR_contextWithParent(NSManagedObjectContext.MR_defaultContext())
与尝试 #1 的结果相同。
尝试 #4 - 从 Hal 的 Answer 中我了解到,即使我创建了两个 MOC,它们都引用了相同的 NSPersistentStore。所以我创建了另一个新商店来保存我的 AppDelegate 中的临时数据。
MagicalRecord.setupCoreDataStackWithStoreNamed("Records")
MagicalRecord.setupCoreDataStackWithStoreNamed("Records-Temp")
然后当我更改日期以获取新数据时,我将那个临时存储设置为默认存储。
func getDate(date: NSDate) {
let url = NSPersistentStore.MR_urlForStoreName("Records-Temp")
let store = NSPersistentStore(persistentStoreCoordinator: NSPersistentStoreCoordinator.MR_defaultStoreCoordinator(), configurationName: nil, URL: url, options: nil)
NSPersistentStore.MR_setDefaultPersistentStore(store)
let context = NSManagedObjectContext.MR_defaultContext()
viewModel.populateDatabase(date, context: context)
}
请注意,我使用的是默认上下文。我得到了数据,但它与尝试 1 和 3 的结果相同。我得到 20 条记录。它们包括旧日期和新日期的数据。如果我使用NSManagedObjectContext.MR_context(),它只会像尝试 1 一样崩溃。
我还发现了其他东西。在 App Delegate 中创建商店后,我在今日视图中打印出默认的商店名称println(MagicalRecord.defaultStoreName())。奇怪的是,它没有打印出我给商店起的名字 Records。相反,它显示了 Reports.sqlite。报告是项目的名称。很奇怪。
为什么我也会得到旧数据?初始化新上下文时我在做什么?
对不起,如果我的问题有点令人困惑,所以我将一个演示项目上传到我的 Dropbox。希望这会有所帮助。
感谢任何帮助。
谢谢。
【问题讨论】:
-
1.使用获取的结果控制器来管理表格视图中的结果。 2. 在构造谓词时使用日期字段作为您的 sortDescriptor - 这样您就不会得到意外的结果。我会花点时间写一篇文章来回答以上所有问题,我现在很忙,但一定要调查一下。另外,神奇的记录有一个 saveWithBlock 功能,你应该使用它来保存
-
很遗憾我不能使用
NSFetchedResultsController。我努力了。我在上面发布的示例是我正在从事的实际项目的一个大大简化的版本。在该应用程序中,数据以复杂的方式进行操作,无法在获取的结果控制器中复制。另外我不使用 saveWithBlock 的原因是数据获取需要根据客户的要求同步。我知道这很糟糕。谢谢你。在此期间我会继续努力。 -
您几乎肯定希望所有内容都在同一个持久存储中。您正在为过去/未来视图创建并显示哪些实体的实例。
-
@HalMueller 在我的实际应用中,原始列表显示了当前日期的
Handover对象列表。用户可以根据需要更改日期并显示不同日期的移交。
标签: ios swift core-data nsmanagedobjectcontext magicalrecord