我正试图让我的脑袋围绕核心数据的NSManagedObjectContext。如果在创建新项目时选中Core Data复选框,则Xcode 10.1提供了大量样板。但我觉得有点混乱,如何为每个视图控制器设置当前上下文。我想我有更好的方法,我正在寻求建议,以确认,或让我回到正确的轨道。
例如,在样板文件AppDelegate代码中,didFinishLaunchingWithOptions为MasterViewController提供了这样的上下文:
let masterNavigationController = splitViewController.viewControllers[0] as! UINavigationController
let controller = masterNavigationController.topViewController as! MasterViewController
controller.managedObjectContext = self.persistentContainer.viewContex
在MasterViewContoller中,第一次使用上下文从fetchedResultsController中获取它并且有代码来保存提供的上下文,即使AppDelegate已经有一个saveContext()函数可用于执行相同的操作:
@objc
func insertNewObject(_ sender: Any) {
let context = self.fetchedResultsController.managedObjectContext
let newEvent = Event(context: context)
// If appropriate, configure the new managed object.
newEvent.timestamp = Date()
// Save the context.
do {
try context.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
在我的具有多个视图控制器的应用程序中,我犯了一些错误,试图在每个需要它的地方重新声明或移交上下文,因此不得不应对由于无意中有多个上下文飞来造成的错误。
所以我的问题是:我犯了一个错误,或者是否存在以下方法的缺点:
1)使AppDelegate成为单身人士:
class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate {
var window: UIWindow?
static let shared = AppDelegate()
…
2)在每个需要它的类中,总是定义上下文(我假设我只需要一个),如下所示:
let context = AppDelegate.shared.persistentContainer.viewContext
3)每当需要保存上下文时,请执行以下操作:
AppDelegate.shared.saveContext()
这似乎更简单,更清晰,更不容易出错,并且似乎在我的实现中起作用。我有没有看到这个问题?
说实话,Apple示例/模板对于初学者来说总是不好的例子,因为它们只显示一件事并且在休息时“破解”(例如,强行打开所有内容)。初学者倾向于只复制这种方法。
免责声明:我在谈论中大型应用程序。您总是可以在小型应用程序中打破此规则和建议,因为不使用它们可以更容易并且导致更简单的应用程序。
在99%你不应该自我实例化AppDelegate
。它由UIApplication
/ @UIApplicationMain
注释为您处理。
AppDelegate
已经是单身人士,因为每个应用程序在整个生命周期中只有一个代表。您可以通过UIApplication.shared.delegate as? AppDelegate
访问它。
但你不应该。 AppDelegate
通过为系统和代码之间的通信提供入口点,在每个应用程序中扮演特定角色,您不应该向其添加其他角色(作为句柄数据库)。在大多数情况下,在代码库中的某个地方访问代码异味和糟糕的架构。
DataBase访问是很好地使用Singleton模式的少数几个例子之一。但是,您应该制作单独的服务,而不是使用AppDelegate
,它只负责处理与coredata的通信(例如创建和处理堆栈,发送查询等)。
所以CoreDataService
是要走的路。
使用单例并不意味着你可以通过输入Singleton.shared
来访问它。这将大大降低组件的可测试性,并使它们与单例高度耦合。
相反,你应该阅读有关Dependency injection principle并注入你的单身人士。例如:
class MyViewController: UIViewController {
let dataBaseManager: CoreDataService
init(with dataBaseManager: CoreDataService) {
self.dataBaseManager = dataBaseManager
super.init(nibName: nil, bundle: nil)
}
}
理想情况下,你应该更进一步到SOLID并向控制器提供它真正需要的东西:
protocol EventsProvider {
func getEvents(with callback: [Event] -> Void)
}
extension CoreDataService: EventsProvider {
func getEvents(with callback: [Event] -> Void) {
// your core data query here
}
}
class MyViewController: UIViewController {
let eventsProvider: EventsProvider
init(with eventsProvider: EventsProvider) {
self.eventsProvider = eventsProvider
super.init(nibName: nil, bundle: nil)
}
}
let vc = MyViewController(with: CoreDataService.shared)
拥有多个NSManagedObjectContext
可以很方便并提高性能,但前提是您知道如何使用它们。
这是更高级的主题,所以你现在可以忽略它。
你可以在Core Data Programming Guide上阅读它