如何在 coredata/NSManagedObject 模型数据更改与应用程序用户界面之间进行紧密耦合?
Posted
技术标签:
【中文标题】如何在 coredata/NSManagedObject 模型数据更改与应用程序用户界面之间进行紧密耦合?【英文标题】:How to do tight coupling between coredata/NSManagedObject model data changes with apps user interface? 【发布时间】:2016-05-25 09:19:03 【问题描述】:TL;DR - 为了简化整个描述,当核心数据对象属性在后台频繁更新时,如何实现非基于 tableview 的视图控制器 UI。并且视觉数据表示需要立即更新。 [在 tableview 中它很容易处理,您使用 FRC 并在对象更新时通过 FRC 委托方法重新加载行。]
我知道NSFetchedResultsController
并通过它映射到用户界面。但这仅适用于用户界面基于列表的情况。
在我的情况下,多个类似类型的硬件通过蓝牙 (BLE) 连接到我的应用程序,并且每个硬件通常以 1 秒的频率提供更新。例如温度、电荷变化。
下面是一个 UI 流程示例,
列表 -> 详细信息
详情
-
健康
活动
通知设置
Details 仅代表与硬件相关的数据(序列号、固件版本、制造日期等),它有 3 个上面提到的按钮,轻按时会推送到相应的控制器。
Entity 模型也是相应地设计的,这里是一瞥,我有一个主要实体,比如说ABC
。然后ABC
与HealthDetails
、Activity
、NotificationSetting
和HardwareDetails
等其他实体具有一对一的关系。 ABC
很少有 identifier
、connected
、name
等属性。
ListViewController
是UITableViewController
,因此我使用的是NSFetchedResultsController
。现在剩下的所有其他视图控制器只是普通的视图控制器,只有标题标签,如“最后收费日期”,它是静态的,下面有一个显示数据的“描述标签”。其他视图控制器也类似地仅填充按钮、标签等。
使用NSFetchedResultsController
可以轻松更新列表视图控制器。
[当用户点击行/单元格时,我只是使用 FRC 中的 indexpath 获取对象并将其注入目标视图控制器。]
但是,其他视图控制器不是基于表视图的。所以我使用通知来控制其他视图控制器 UI 更新。
例如,当我的 BluetoothManager
从硬件接收到有关特性的更新时,我将特性 id 和数据引导到我的 DatabaseHelper
,然后它将数据解码并插入到相应的托管对象中,然后在此我只是用更新的对象的标识符发出通知。
导航堆栈中层次结构中的所有UIViewController
s 都被订阅为观察者。如果通知中的标识符与当前显示的实体对象匹配,那么我会刷新 UI。目前这一切都很好。
但是,我觉得它很笨拙,要处理很多通知。这是将单个核心数据模型与 UI 紧密耦合的唯一方法还是存在更好的方法?
我看到少数 cmets 建议仅通过 FRC 处理所有内容,但我不确定 FRC 是否可以用于非基于 tableview 的 UI 表示。我已经搜索并没有找到太多。如果有人知道任何具有理论描述的教程/博客,那将是很大的帮助。
关于 FRC 处理所有的建议的另一件事,我没有完全理解,我需要为每个视图控制器放置一个 FRC 吗?没有别的办法吗?
【问题讨论】:
FRC 不仅适用于基于列表的界面,您是否考虑过如何将它用于您的其他屏幕?你真的没有解释你的数据模型或你的屏幕,为什么你认为 FRC 不适合...... @Wain 你好,wain,我太忙了,没有时间写一个更详细的问题。没有其他的。但是,我只是就如何处理结构提出了更多观点。如果您有更好的解决方案,请您研究一下并回答?如果这么多细节还不够,请告诉我。 【参考方案1】:你不应该失去任何东西。 NSFetchedResultsController 上的委托会在任何时候告诉你有什么更新。如果你有一个可见的 UI,那么你的 UI 将会更新。如果您没有可见的 UI,或者不想要一个,您仍然可以将该 FRC 链接到您的特定 NSManagedObject 子类以获取运行状况详细信息,然后根据发生变化的事实采取任何适当的措施。
取决于你在做什么,它可能就像实现controllerDidChangeContent(_:)
一样简单
如果这不能解决您的疑虑,您将不得不提供有关具体问题的更多详细信息。
【讨论】:
感谢您的回答。我完全明白你在这里说什么。然而,委托是一对一的模式。因此,如果我从列表 > 健康 > 活动 > 设置推送我的视图控制器,那么顶部视图控制器现在是设置。因此,根据您的回答,当前设置视图控制器是委托。但是当我弹回来时,我希望所有(活动、健康和列表)UI 也更新。你能详细说明如何处理这个问题吗?如果您还需要更多详细信息,请告诉我更多信息,我会更新问题。 现在要提一下,我只是将模型从 List > Health > Activity > Settings 传递到层次结构中,而不是尝试在每个视图控制器中获取。 你应该传递你的 NSManagedObjectContext,可能还有一个或两个实体,但不是你的整个模型。您可能希望每个视图控制器都有自己的 NSFetchedResultsController 来管理特定视图控制器正在查看的部分。您甚至可能在每个视图控制器中拥有多个视图控制器,这没关系!【参考方案2】:您提到更改“ViewControllers”,所以我假设当您按您在 cmets 中所说的“列表 > 健康 > 活动 > 设置”推送时,每个都是 UIViewController
类,对吧?如果是这样,请在每一个中实现viewWillAppear
方法,并确保在那里进行所有的 UI 初始化。通过这样做,您可以保证当弹出发生时,将调用此方法并更新您的 UI(b/c 您现在在此方法中具有执行更新的逻辑)。我曾经在 viewDidLoad
中进行 UI 初始化,但现在遇到了同样的问题。在我的情况下,将其移至 viewWillAppear
是解决方案。
在此之后,如果您仍然没有在 UI 中看到正确的值,请在 viewWillAppear
方法中放置断点并检查模型中的数据。也许模型不正确——在这种情况下,也许您执行查询(搜索)以再次获取您需要的内容。除非您使用父/子上下文并且没有将子上下文更改提交给父级,否则这不应该成为问题。
但我觉得它很笨拙,要处理很多通知。是个 存在紧密耦合的唯一方法或任何更好的方法 单个核心数据模型到 UI。
老实说,我认为没有更好的模型,因为您没有在其他视图上使用 FRC。如果您不使用表视图重新加载或获取新数据,那么您绝对不需要使用 FRC(尽管我不会说你不应该,因为我无法坚定地支持这一点)。
我在我的应用程序中使用了这个精确的模型,它有几个不同的视图,它们通过观察者接收通知——就像你做的一样。如果这效果不佳,则可以考虑另一种选择。
【讨论】:
我正在 viewWillAppear 中触发 UI 设置,但问题是当您处于顶视图控制器(非基于表视图)中时如何进行,并且核心数据对象属性在背景。你明白了。您需要了解如何通知您以触发刷新。 @Rameswar,在这种情况下,您的通知模型应该正常工作。您能否确认订阅(已加载)的每个视图都收到了通知?当您收到这些通知时,接收通知的方法会做什么?【参考方案3】:抱歉,我没有足够的声望来添加评论。
我会加入其他建议使用 FRC 的行列。我无法为您提供博客/教程,但CoreStore ObjectMonitor 是一个很好的例子,因为它可以在不同的(非表格视图)上下文中使用。我相信你得到它不会有问题。
【讨论】:
感谢您的链接,这看起来很有希望。我只是浏览了一下,需要玩一段时间才能了解实现。【参考方案4】:因此,您已经有了一种解决问题的方法,即使用通知。这没有什么明显的错误。可以通过仅观察您的 VC 感兴趣的 ID 来改进它,以防止多个 VC 处理并检查通知是否适用于它们。这是一个有用的解决方案。
FRC 易于与基于表格/网格的布局一起使用,但这并不是它的全部优点,因为它提供了特定的上下文观察。在***别,这意味着仅响应 controllerDidChangeContent:
委托回调以更新 UI。这通常比您的通知要好,因为它是具体的,尽管它在内存消耗方面的成本更高(这很小以至于无关紧要)。
如果您的详细 VC 显示多条信息,那么您可能没有使用表格/集合视图,但仍有列表/网格样式布局,并且 FRC 索引路径可能意味着与此相关的内容。
尚未讨论的替代方案是 KVO,它被所有托管对象本机支持。这是最适合您的解决方案,因为您正在传递对象,因此您不需要进行任何更高级别/上下文观察。直接使用 KVO 有时会有点痛苦,所以你想使用一些帮助库,比如this one,这样你就可以使用块来处理专门更新的项目。
使用 KVO 可以获得数据绑定,因此作为详细观察回调的结果,对底层模型数据的更新会直接反映在您的 UI 上。
【讨论】:
请注意,如果您使用 FRC,那么是的,每个 VC 都会有自己的 FRC【参考方案5】:首先,我了解您对为此使用一对一模式的担忧。 (仅适用于协议:它不是一对一的模式,因为委托可以是许多委托类的委托。实际上是这样的。)
to-many 模式确实是通知:它是广播。但我不明白,为什么你有这么多通知?只需收听上下文的NSManagedObjectContextDidSaveNotification
,它将为您提供已更改对象的列表。细节视图控制器应该很容易拾取它显示的对象。
我误会了吗?
【讨论】:
而且我相信在这种情况下,如果 View Controller 能够选择它显示的对象,则必须使用“objectID”来完成,我认为它在托管对象上下文生命周期中不可变循环。是这样吗? 第一次保存后不可变(持久ID)。这是因为它包含 rDBMS 的主键,该主键在创建时未设置。 (否则,如果您创建大量中间对象,CD 会出现问题。)但这应该没有问题,因为您应该在创建后立即保存新对象,因此在首次显示之前保存。所以你的 VC 只能看到持久 ID。对于 CD,相等的对象是相同的,当然,反之亦然。-isEqual:
比较对象 ID。以上是关于如何在 coredata/NSManagedObject 模型数据更改与应用程序用户界面之间进行紧密耦合?的主要内容,如果未能解决你的问题,请参考以下文章
如何在异步任务中调用意图?或者如何在 onPostExecute 中开始新的活动?