Ios NSManagedObjectContextObjectsIDChange通知仅在以前访问过修改的托管对象时发送。为什么?

Ios NSManagedObjectContextObjectsIDChange通知仅在以前访问过修改的托管对象时发送。为什么?,ios,swift,core-data,nsnotificationcenter,Ios,Swift,Core Data,Nsnotificationcenter,(有关更多详细信息,请参阅下面的更新。) 这是一个奇怪的问题。我不能在我的课堂上发布所有内容(太大了),所以我将尝试涵盖基本部分: 我有一个带有viewDidLoad()方法的视图控制器: class MyClass { ... override func viewDidLoad() { super.viewDidLoad() // mainViewContext is the viewContext passed by AppD

(有关更多详细信息,请参阅下面的更新。)

这是一个奇怪的问题。我不能在我的课堂上发布所有内容(太大了),所以我将尝试涵盖基本部分:

我有一个带有
viewDidLoad()
方法的视图控制器:

class MyClass {
    ...
    override func viewDidLoad() {
        super.viewDidLoad()        

        // mainViewContext is the viewContext passed by AppDelegate's persistentContainer
        NotificationCenter.default.addObserver(self, selector: #selector(test(notif:)), name: Notification.Name.NSManagedObjectContextObjectsDidChange, object: mainViewContext)
    }
    ...
    @objc func test(notif: Notification) {
        print("I got called")
    }
    ...
所以我所做的就是添加一个观察者,这样我就知道CoreData对象是否已更改。但是,此通知并不总是发布。当我这样做时(例如在添加观察者之后的
viewDidLoad()
中):

它正在工作<正在调用代码>测试(notif:)。但现在我将其替换为:

self.appDelegate.persistentContainer.performBackgroundTask({ 
(privateContext) in
    // Just another type of managed object I have.
    let folder = privateContext.object(with: App.templateRackObjectID) as! Rack
    folder.name = "newFolderName" + String(Int.random(between: 0, and: 90000))

    do {
        try privateContext.save()
    } catch let error as NSError {
        print("Error: " + error.debugDescription)
    }
})
它不再工作了<代码>测试(notif:)未被调用。我不知道这是为什么。我只是改变了正在修改的对象

(我在AppDelegate中设置了
persistentContainer.viewContext.automaticallyMergesChangesFromParent=true

通知是否仅发布在某些类型的托管对象上?有什么我错过的吗?我不知道如何开始调试这个。我已经试了好几个小时了。这是非常奇怪的

有什么想法吗?如果你需要更多信息,请告诉我。正如我所说,我不知道什么对这个问题很重要。整个班级都太大了,不能在这里张贴

更新: 我试图用一个虚拟项目来重现这个问题。奇怪的是,它没有发布任何一个托管对象的通知。我已将其上载到github:

也许我做错了什么事,我看不出来

更新2(发现了一些东西。我更新了回购协议) 这真是不可思议。我怀疑这是真的(我猜)

似乎只有在您之前加载了要修改并访问其属性的同一托管对象时,才会发布通知。在启动
performbackground任务之前,我添加了以下内容:

// As a class attribute, I added `private var rack: Rack!`. Then this is in `viewDidLoad`, right before the background task
   rack = mainViewContext.object(with: App.templateRackObjectID) as! Rack
            _ = rack.name
现在它开始工作了。但是为什么呢?这很奇怪。为什么我以前必须访问它?应始终发送通知。有什么想法吗? 如果您想尝试,我将它添加到Github上的虚拟项目中


我真的非常感谢您的帮助:)

今天也偶然发现了这个。上下文不会保留对象,因此不会“更新”您的对象。我发现将
retainsRegisteredObjects
设置为
true
将导致上下文开始发送预期的通知。

您是否尝试将对象设置为addObserver为nil

在我看来,您是从一个不同于您所更改的上下文的上下文中观察通知的。在这里,它表示“每次调用此方法时,持久化容器都会创建一个新的NSManagedObjectContext。”因此,当您执行BackgroundTask时,您是在一个全新的上下文中进行更改,而您只是在侦听mainContext的更改,您根本不应该收到任何通知?我很惊讶它第一次起作用

当automaticallyMergesChangesFromParent时,这里说:“指示上下文是否自动合并保存到其持久存储协调器或父上下文的更改。”我没有看到在performBackgroundTask调用中创建的新上下文是任何其他上下文的子上下文,可能也不是这里的mainContext。但是,当privateContext保存时,协调器应该会收到一些更改,但协调器也不会发送通知

如果要将后台上下文合并到主上下文中,并且只从主上下文接收通知,则可能需要

// As a class attribute, I added `private var rack: Rack!`. Then this is in `viewDidLoad`, right before the background task
   rack = mainViewContext.object(with: App.templateRackObjectID) as! Rack
            _ = rack.name