存储对已删除的NSManagedObjects

时间:2017-05-05 05:45:24

标签: ios core-data nsmanagedobject

当我从数据库中删除NSMangedObject时,分配给它的局部变量会发生什么?

例如,我有一个简单的NSManagedObject:

class MyManagedObject: NSManagedObject {
    @NSManaged var name: String
}

然后在我的ViewController中,我将它从数据库中拉出来,然后在本地分配:

class ViewController: UIViewController {
     var myManagedObject: MyManagedObject!
}

然后我从数据库中删除它。

如果打印对象名称,我会在控制台中获得以下内容

print("myManagedObject.name = \(myManagedObject.name)")
//prints: "myManagedObject.name = "

好像没有那个物体?但是,如果我将变量变为可选项并将其检查为零,我被告知它不是零。

我不太确定如何在脑海中调和这一点。似乎有一些东西指向局部变量,但它的属性已经消失。

如果我有许多不同的UI对象依赖于该对象的属性,我不能假设它在内存中有一些本地深层副本吗?

以下是更完整的代码:

在viewDidLoad中,我创建新对象,保存上下文,获取对象,然后在本地分配。

class ViewController: UIViewController {

      var myManagedObject: MyManagedObject!

      override func viewDidLoad() {
            super.viewDidLoad()

            //1 Create the new object
            let newObject = NSEntityDescription.insertNewObject(forEntityName: "MyManagedObject", into: coreDataManager.mainContext) as! MyManagedObject
            newObject.name = "My First Managed Object"

            //2 Save it into the context
            do {
                try coreDataManager.mainContext.save()
            } catch {
                //handle error
            } 

            //3 Fetch it from the database
            let request = NSFetchRequest<MyManagedObject>(entityName: "MyManagedObject")
            do {
                let saved = try coreDataManager.mainContext.fetch(request)
                //4 Store it in a local variable
                self.myManagedObject = saved.first
            } catch {
                 //handle errors
            }
      }
}

此时如果我打印局部变量的name属性,我得到正确的答案:

print("The object's name is: \(myManagedObject.name)")
//prints: The object's name is: My First Managed Object

所以,现在我从数据库中删除它:

if let storedObject = myManagedObject { 
     coreDataManager.mainContext.delete(storedObject)
     do {
         try coreDataManager.mainContext.save()
     } catch {
         //handle error
     }
}

但现在,当我打印时,我得到了最奇怪的输出:

print("myManagedObject.name = \(myManagedObject.name)")
//prints: "myManagedObject.name = "

这完全不是我期待内存工作的方式。如果我创建了一个类Foo的实例,然后将该实例传递给不同的对象,那么它就是同一个实例。一旦没有人指向它,它就会消失。

在这种情况下---变量是什么,myManagedObject?这不是nil。什么是字符串,name?这是一个空字符串吗?或者是其他奇怪的元类型?

1 个答案:

答案 0 :(得分:0)

您可能正在寻找的主要内容是核心数据上下文。上下文是内存和实际数据库之间的连接。

每当您获取数据时,都会通过上下文获取数据。这些是可以修改甚至删除的托管对象。在保存上下文之前,这些都不会真正发生在数据库上。

当你删除一个对象时,它被标记为删除,但它不会从内存中删除,它不能是因为如果没有别的,上下文仍然会使用它来实际从数据库本身删除该对象。

一旦您调用删除它,托管对象会发生什么变化几乎无关紧要,即使记录它可能会因为它是框架的一部分而发生变化。因此,您有责任检查这些情况并在需要时重新获取对象。因此,您必须确保您的应用程序具有适当的体系结构并负责任地使用核心数据。

关于如何使用数据库的方法有很多种,或多或少都有一种以最佳方式使用数据库的独特方式。您需要更具体地了解自己在做什么以及在哪里看到潜在问题,以便我们能够让您走上正确的轨道。

举个例子来考虑远程服务器的数据同步。在这里,您希望无论用户在做什么或应用程序的哪个部分,都可以随时同步数据。

为此,我建议你有一个单独的上下文,它在一个单独的线程上运行。应该包装所有托管对象,并从数据库中检索其属性。在大多数实体上你会有类似的东西:

MyEntity.findAll { items in
    ...the fetch happens on context thread and returns to main, items are wrappers
}
MyEntity.find(id: idString, { item in
    ...the fetch happens on context thread and returns to main, items are wrappers
})()

然后,由于您无法直接访问托管对象,因此您需要某种方法将数据复制到托管对象,如:

myEntityInstance.commit() // Copies all the data to core data object. The operation is done on a context thread. A callback is usually not needed

然后保存数据库

MyEntity.saveDatabse {
   ... save happens on the context thread and callback is called on main thread
}

现在智能部分是saveDatabse方法将向代表报告已经进行了更改。委托通常是当前的视图控制器,因此有一个像DataBaseViewController这样的超类有意义,它在视图上看起来像是委托MyEntity.delegate = self,在视图上加载调用一些方法reloadData并在databaseDidChange委托方法中调用reloadData,并在viewWillAppear中调用相同内容。

现在,作为DataBaseViewController子类的每个视图控制器都将覆盖reloadData,在该方法中,您将再次从数据库中获取数据。您要么获取所有项目还是单个项目。因此,对于那些只需要保存对象的id并按id再次获取它。如果返回的对象是nil,那么项目已被删除,因此您可以抓住您似乎提到的问题。

所有这些都过于简单,但我希望您对核心数据以及如何使用它有一个基本的想法。这并不容易,它从来没有,它很可能永远不会。它旨在提高速度,即使在非常大的数据库中也能够在最短的时间内访问数据。结果是它可能不太安全。