如何处理有关核心数据、共享偏好和通知的 Mac OS X 助手/主应用程序架构?

Posted

技术标签:

【中文标题】如何处理有关核心数据、共享偏好和通知的 Mac OS X 助手/主应用程序架构?【英文标题】:How to deal with Mac OS X Helper/Main app architecture regarding core data, shared preferences and notifications? 【发布时间】:2012-12-28 05:05:53 【问题描述】:

我对我正在处理的一个项目(Mac OS X 应用程序)有一些架构上的疑问。它基本上由两个元素组成:在后台运行并收集一些数据的守护进程和用于表示收集到的数据的查看器。

该守护程序应在状态栏中可见(无停靠图标),并包含一个可通过状态栏访问的小菜单。它将数据保存在核心数据存储中。其中一个菜单项是一个打开查看器链接。打开此查看器时,普通 GUI 应用程序应开始包含停靠图标和菜单栏。查看器在打开应用程序本身时也会打开(通过双击图标)。

经过一些试验,我发现实现此功能的最佳方法是创建两个应用程序,主应用程序代表 查看器,辅助实用程序代表 守护进程 .我这样做的原因之一是无法立即在 LSUIElement 值之间切换以强制守护程序/查看器状态。

现在我对这个架构有一些疑问:

守护程序和查看器应用程序都使用相同的核心数据存储来保存和检索数据。当拥有一个多线程应用程序时,我知道需要多个 NSManagedObjectContext 对象才能正确同步数据。让多个应用程序同时使用同一个核心数据存储会怎么样?这甚至可以在没有冲突、锁定等风险的情况下实现吗?如何保证一致性?

守护程序应始终在查看器启动时启动。我通过简单地循环所有打开的进程并检查是否列出了守护程序的捆绑标识符来实现这一点。如果不是,则使用NSWorkspacelaunchApplication 启动守护进程。这工作正常。现在,当用户退出守护程序时,查看器也应该停止。通知查看者守护程序停止的最佳方式是什么?如果守护进程消失,我可以定期检查活动进程并退出查看器,但这听起来有点奇怪。我宁愿选择在查看器即将关闭时发送的某种通知。但是由于应该在应用程序之间发送和捕获此通知,因此我不知道哪个 simple 通知服务可用。有什么想法吗?

应用程序被沙盒化,因为它将在 Mac App Store 上分发。使用NSWorkspacelaunchApplication 启动应用程序会导致目标应用程序在与源相同的沙盒环境中运行,我认为这根本不是问题,因为在同一个沙盒中运行两个应用程序感觉更好 可能是。但是想象一下这种情况:在登录时会自动启动守护进程(使用SMLoginItemSetEnabled)并且用户双击 Viewer.app。由于守护进程已经在运行(同样,这是通过循环通过活动进程来检查的)它不会被启动。现在我们让守护进程和查看器在不同的沙箱中运行,对吧?这会导致偏好、核心数据存储等方面的任何问题吗?

我想使用NSUserDefaults 进行基本配置,我可以在守护程序和查看器之间以某种方式交换这些数据吗?同样,两个应用程序将具有不同的包标识符。

提前感谢您的帮助,不胜感激!

【问题讨论】:

似乎我在创建一个可以与 2 个不同应用程序通信和共享设置(只是没有 Mac App Store 和沙盒)的守护程序/助手工具时遇到了类似的问题,您是否以某种方式解决了它?跨度> 您的问题很有趣,我想知道一些解决方案。将它们分开可能更容易得到答案? 你运气好了吗?我想聊聊这个话题。你可以在 Twitter 上找到我:@ctietze 最终我决定开发一个应用程序而不是开发两个独立的应用程序。但是,我相信本文中写的许多挑战可以通过使用 NSDistributedNotificationCenter (developer.apple.com/library/mac/documentation/Cocoa/Reference/…) 允许应用程序相互通信来解决。最后,我认为应该只有一个应用程序负责保存数据和访问首选项(这个应用程序将充当另一个应用程序和数据之间的代理)。 使用 [[NSUserDefaults initWithSuiteName:]](developer.apple.com/library/prerelease/ios//documentation/Cocoa/…) 的应用套件可以轻松完成共享首选项。 【参考方案1】:

这个问题没有一个正确的答案,但这是我的解决方法:

守护程序和查看器应用程序都使用相同的核心数据存储来保存和检索数据。

因为不支持在进程之间共享核心数据存储(据我所知),我会让守护进程公开一个XPC Service。查看器不会打开 Core Data 存储本身,而是使用 NSXPCConnection 通过守护进程访问数据。

假设查看器永远不会在没有守护程序的情况下运行,它可以使用SMLoginItemSetEnabled,就像您在问题中提到的那样,为守护程序注册一个 mach 服务,然后连接到该服务。

在 Apple 的网站上有一个示例代码详细介绍了设置 here 的详细信息(摘要:守护程序需要位于 App.app/Contents/Library/LoginItems/daemon.bundle.id.app),您可能还想阅读 this blog post 讨论一些额外的要求沙盒强制执行(总结:额外确保您的团队 ID 在守护程序的包标识符中)。

守护程序应该总是在查看器启动时启动。

全部设置:一旦您使用SMLoginItemSetEnabled 注册守护程序,launchd 将在查看器连接到其 XPC 服务时启动它(如果需要)。

现在当用户退出守护进程时,查看器也应该停止。

查看者可以使用NSXPCConnection 找出守护程序何时退出。守护进程还可以在退出之前使用SMLoginItemSetEnabled 取消注册,这样它就不会重新启动。

我想使用NSUserDefaults 进行基本配置,我可以在守护进程和查看器之间以某种方式交换这些数据吗?同样,两个应用程序将具有不同的包标识符。

为此使用套件:

// To read or write:
NSUserDefaults* suiteDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"com.example.app.shared"];
[suiteDefaults setObject:[NSDate date] forKey:@"launchTime"];

// Add the suite to -standardUserDefaults to make reading easier:
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
[defaults addSuiteNamed:@"com.example.app.shared"];

要使用沙盒,查看器和守护程序必须共享一个应用组。您甚至可以使用 KVO 来观察共享密钥的变化。

【讨论】:

以上是关于如何处理有关核心数据、共享偏好和通知的 Mac OS X 助手/主应用程序架构?的主要内容,如果未能解决你的问题,请参考以下文章

如果您在事先同意通知后禁用通知,Apple 如何处理推送令牌?

如何处理和发送远程通知 Objective-C

核心数据:如何处理新版本?

应用未运行时如何处理远程通知

如何处理 FCM 通知而不在 IOS 上键入弹出窗口

如何处理收到推送通知后恢复应用程序的情况