NSFetchedResultsController 的关系未更新
假设我有两个实体,Employee
和 Department
。部门与员工是一对多的关系,每个部门可以有很多员工,但每个员工只能属于一个部门。我想使用 NSFetchedResultsController
在表视图中显示所有员工,该表视图按其所属部门的属性数据排序。问题是,我希望当部门对象收到更改时更新我的表,就像员工的常规属性发生更改一样,但 NSFetchedResultsController 似乎不跟踪相关对象。我通过执行以下操作部分解决了这个问题:
for (Employee* employee in department.employees) {
[employee willChangeValueForKey:@"dept"];
}
/* Make Changes to department object */
for (Employee* employee in department.employees) {
[employee didChangeValueForKey:@"dept"];
}
这显然不理想,但它确实会导致基于员工的 FRC 委托方法 didChangeObject 被调用。我现在剩下的真正问题是对跟踪员工对象的 FRC 进行排序:
NSEntityDescription *employee = [NSEntityDescription entityForName:@"Employee" inManagedObjectContext:self.managedObjectContext];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"department.someProperty" ascending:NO];
这非常有效,并且在第一次调用时对员工进行正确排序,问题是当我对应该更改的部门的某些属性进行更改时对我的员工表进行排序,没有任何反应。有什么好的方法可以让我的员工 FRC 跟踪关系中的变化吗?特别是,当排序基于相关属性时,我只需要某种方法让它更新排序。我浏览过一些类似的问题,但未能找到令人满意的解决方案。
Let's say I have two entities, Employee
and Department
. A department has a to-many relationship with an employee, many employees can be in each department but each employee only belongs to one department. I want to display all of the employees in a table view sorted by data that is a property of the department they belong to using an NSFetchedResultsController
. The problem is that I want my table to update when a department object receives changes just like it does if the regular properties of employee change, but the NSFetchedResultsController
doesn't seem to track related objects. I've gotten passed this issue partially by doing the following:
for (Employee* employee in department.employees) {
[employee willChangeValueForKey:@"dept"];
}
/* Make Changes to department object */
for (Employee* employee in department.employees) {
[employee didChangeValueForKey:@"dept"];
}
This is obviously not ideal but it does cause the employee based FRC delegate method didChangeObject to get called. The real problem I have left now is in the sorting a FRC that is tracking employee objects:
NSEntityDescription *employee = [NSEntityDescription entityForName:@"Employee" inManagedObjectContext:self.managedObjectContext];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"department.someProperty" ascending:NO];
This works great and sorts the employees correctly the first time it's called, the problem is that when I make changes to some property of a department that should change the sorting of my employee table, nothing happens. Is there any nice way to have my employee FRC track changes in a relationship? Particularly I just need some way to have it update the sorting when the sort is based on a related property. I've looked through some similar questions but wasn't able to find a satisfactory solution.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
NSFetchedResultsController
的设计目的只是一次观察一个实体。您的设置虽然有意义,但有点超出了 NSFetchedResultsController 目前能够自行观看的范围。我的建议是设置你自己的观察者。您可以将其基于我在 GitHub 上设置的 ZSContextWatcher,或者您也可以使其更加简单。
基本上,您希望监视
NSManagedObjectContextDidSaveNotification
帖子,然后在包含您的部门实体的火灾发生时重新加载表。我还建议向 Apple 提交 rdar 并要求将
NSFetchedResultsController
改善了。The
NSFetchedResultsController
is only designed to watch one entity at a time. Your setup, while it makes sense, it a bit beyond what theNSFetchedResultsController
is currently capable of watching on its own.My recommendation would be to set up your own watcher. You can base it off the ZSContextWatcher I have set up on GitHub, or you can make it even more straightforward.
Basically, you want to watch for
NSManagedObjectContextDidSaveNotification
postings and then reload your table when one fire that contains your department entity.I would also recommend filing a rdar with Apple and asking for the
NSFetchedResultsController
to be improved.Swift
因为 NSFetchedResultsController 是为一次一个实体设计的,所以您必须监听 NSManagedObjectContextObjectsDidChangeNotification 才能收到有关所有实体关系更改的通知。
这是一个例子:
Swift
Because the NSFetchedResultsController is designed for one entity at a time, you have to listen to the NSManagedObjectContextObjectsDidChangeNotification in order to be notified about all entity relationship changes.
Here is an example:
这是 NSFetchedResultsController 的一个已知限制:它仅监视实体属性的更改,而不监视其关系属性的更改。但你的用例是完全有效的,这里是如何克服它。
工作原理
在浏览了很多可能的解决方案之后,现在我只需创建两个
NSFetchedResultsController
:第一个(在您的情况下为Employee
),以及另一个用于监视上述关系中的实体(Department
)。然后,当Department
实例以应更新您的Employee
FRC 的方式更新时,我只是伪造附属Employee
实例的更改使用 NSFetchedResultsControllerDelegate 协议。请注意,受监视的Department
属性必须是其NSFetchedResultsController
的NSSortDescriptors
的一部分才能正常工作。示例代码
在您的示例中,如果可以这样工作:
在您的视图控制器中:
还要确保您在类声明中声明与
NSFetchedResultsControllerDelegate
的一致性。在
viewDidLoad()
中:在
departmentsFetchedResultsController
创建中:在
NSFetchedResultsControllerDelegate
中> 方法:这就是神奇之处:
每个受影响员工的部门的虚假更新将按预期触发
employeesFetchedResultsController
的正确更新。This is a known limitation of
NSFetchedResultsController
: it only monitors the changes of you entity's properties, not of its relationships' properties. But your use case is totally valid, and here is how to get over it.Working Principle
After navigating a lot of possible solutions, now I just create two
NSFetchedResultsController
: the initial one (in your case,Employee
), and another one to monitor the entities in the said relationship (Department
). Then, when aDepartment
instance is updated in the way it should update yourEmployee
FRC, I just fake a change of the instances of affiliatedEmployee
using theNSFetchedResultsControllerDelegate
protocol. Note that the monitoredDepartment
property must be part of theNSSortDescriptors
of itsNSFetchedResultsController
for this to work.Example code
In your example if would work this way:
In your view controller:
Also make sure you declare conformance to
NSFetchedResultsControllerDelegate
in the class declaration.In
viewDidLoad()
:In the
departmentsFetchedResultsController
creation:In the
NSFetchedResultsControllerDelegate
methods:That's where the magic operates:
This fake update of the department of each impacted employee will trigger the proper update of
employeesFetchedResultsController
as expected.SwiftUI
我还没有看到直接解决 SwiftUI 中这个问题的帖子。在尝试了许多帖子中概述的解决方案并尝试避免编写自定义控制器之后,使其在 SwiftUI 中工作的唯一因素(这是 harrouet 上一篇文章的一部分(谢谢!))是:
利用
FetchRequest
Employee
。例如,如果您关心每个部门的员工数量,那么虚假关系更新在 SwiftUI 中不会产生任何影响。也没有任何
willChangeValue
或didChangeValue
语句。实际上,willChangeValue
导致了我的崩溃。这是一个有效的设置:
我不知道这是否解决了
CoreData
关系未传播到视图的所有潜在问题,并且如果员工数量非常大,它可能会带来效率问题,但它为我工作。另一种方法也可以在不获取所有员工的情况下建立正确的员工数量(这可以解决上述代码片段的效率问题),即创建对
NSFetchRequestResultType.countResultType
类型的视图依赖项>FetchRequest:主视图变为:
同样,这可能无法解决所有可能的关系依赖关系,但它带来了希望,可以通过考虑视图中的各种类型的
FetchRequest
依赖关系来提示视图更新SwiftUI意见。请注意,
DataManager
不需要是在视图中观察的ObservableObject
才能正常工作。SwiftUI
I haven't seen posts that directly addressed this issue in SwiftUI. After trying solutions outlined in many posts, and trying to avoid writing custom controllers, the single factor that made it work in SwiftUI—which was part of the previous post from harrouet (thank you!)—is:
Make use of a
FetchRequest
onEmployee
.If you care about, say, the employee count per department, the fake relationship updates did not make a difference in SwiftUI. Neither did any
willChangeValue
ordidChangeValue
statements. Actually,willChangeValue
caused crashes on my end.Here's a setup that worked:
I don't know if this fixes all the potential issues with
CoreData
relationships not propagating to views, and it may present efficiency issues if the number of employees is very large, but it worked for me.An alternative that also worked for establishing the right employee count without grabbing all employees—which may address the efficiency issue of the above code snippet—is to create a view dependency on a
NSFetchRequestResultType.countResultType
type ofFetchRequest
:And the main View becomes:
Again, this may not resolve all possible relationship dependencies, but it gives hope that view updates can be prompted by considering various types of
FetchRequest
dependencies within the SwiftUI views.A note that
DataManager
needs NOT be anObservableObject
being observed in the View for this to work.