存储对已删除 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 = "

好像对象不存在? 但是,如果我将变量转换为可选变量并检查它是否为 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
}
}
}

此时,如果我打印局部变量的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,字符串是什么?它是空字符串吗?还是其他奇怪的元类型?

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

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

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

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

关于如何使用数据库,有很多方法,其中任何一种都有独特的方法来最佳地使用它。您需要更具体地说明您正在做什么以及您在哪里看到潜在问题,以便我们让您走上正确的轨道。

为了给您一个示例,请考虑从远程服务器进行数据同步。在这里,您希望无论用户在做什么或他是应用程序的哪个部分,都可以随时同步数据。

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

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则项目已删除,因此您可以抓住似乎提到的问题。

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

最新更新