Ios 存储对已删除NSManagedObject的引用的局部变量会发生什么情况

Ios 存储对已删除NSManagedObject的引用的局部变量会发生什么情况,ios,core-data,nsmanagedobject,Ios,Core Data,Nsmanagedobject,当我从数据库中删除NSMangedObject时,分配给它的局部变量会发生什么情况 例如,我有一个简单的NSManagedObject: class MyManagedObject: NSManagedObject { @NSManaged var name: String } 然后在我的ViewController中,我将其从数据库中拉出,并在本地分配: class ViewController: UIViewController { var myManagedObject

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

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

class MyManagedObject: NSManagedObject {
    @NSManaged var name: String
}
然后在我的ViewController中,我将其从数据库中拉出,并在本地分配:

class ViewController: UIViewController {
     var myManagedObject: MyManagedObject!
}
然后我从数据库中删除它

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

print("myManagedObject.name = \(myManagedObject.name)")
//prints: "myManagedObject.name = "
好像物体不在那里?但是如果我把这个变量变成一个可选变量并检查它是否为nil,我会被告知它不是nil

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

如果我有许多完全不同的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
            }
      }
}
因此,现在我将其从数据库中删除:

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
?它是一个空字符串吗?还是其他奇怪的元类型?

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

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

当您删除一个对象时,该对象被标记为删除,但未从内存中删除,因为如果没有其他内容,上下文仍将使用该对象从数据库中实际删除该对象

一旦您调用删除托管对象,它会发生什么几乎是不相关的,即使它可能会更改,因为它是框架的一部分。因此,您有责任检查这些案例,并在需要时重新蚀刻对象。因此,您必须确保您的应用程序具有适当的体系结构,并负责任地使用核心数据

关于如何使用数据库,有很多种方法,其中或多或少都有一种独特的最佳使用方式。你需要更具体地说明你在做什么,你在哪里看到潜在的问题,这样我们才能让你走上正确的轨道

给你一个例子,考虑远程服务器的数据同步。在这里,无论用户在做什么或他是应用程序的哪一部分,都可以随时同步数据

为此,我建议您使用一个在单独线程上运行的上下文。从数据库中检索到所有托管对象后,应将其包装并复制其属性。在大多数实体上,您可能会遇到以下情况:

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
}
现在,聪明的部分是
savedatabase
方法将向代理报告所做的更改。委托通常是当前视图控制器,因此有一个超类(如
DataBaseViewController
)是有意义的,该超类在视图中出现时会将自身指定为委托
MyEntity.delegate=self
,查看时确实加载了一些方法调用
reloadData
,并且在
databaseDidChange
中委托方法调用
reloadData
,并且在
视图中将出现相同的调用

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


所有这些都过于简单化了,但我希望您对核心数据以及如何使用它有一个基本的了解。这并不容易,过去从未如此,将来也很可能如此。它的设计是为了能够在尽可能短的时间内访问数据,即使是从一个非常大的数据库。结果是它可能不是很安全。

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

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

当您删除一个对象时,该对象被标记为删除,但未从内存中删除,因为如果没有其他内容,上下文仍将使用该对象从数据库中实际删除该对象

一旦您调用删除托管对象,它会发生什么几乎是不相关的,即使它可能会更改,因为它是框架的一部分。因此,您有责任检查这些案例,并在需要时重新蚀刻对象。因此,您必须确保您的应用程序具有适当的体系结构,并负责任地使用核心数据

关于如何使用数据库,有很多种方法,其中或多或少都有一种独特的最佳使用方式。你需要更具体地说明你在做什么,你在哪里看到潜在的问题,这样我们才能让你走上正确的轨道

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