我对我正在做的一个项目(Mac OS X应用程序)有一些架构上的疑问。它基本上由两个元素组成:一个在后台运行的守护进程,用于收集一些数据,一个查看器用于表示收集到的数据。
守护进程应该在状态栏中可见(没有dock图标),并且包含一个可以通过状态栏访问的小菜单。它将数据保存在核心数据存储中。其中一个菜单项是链接,它打开查看器。当这个查看器打开时,应该启动一个普通的 GUI应用程序,包括一个dock图标和菜单栏。当打开应用程序本身时,也会打开查看器(通过双击图标)。
经过一些试验后,我发现实现此功能的最佳方法是创建两个应用程序,代表查看器的主应用程序和代表守护进程的辅助实用程序。我这样做的原因之一是不可能在LSUIElement
值之间立即切换以强制守护进程/查看器状态。
现在我对这个架构有一些问题:
守护程序和查看器应用程序都使用相同的核心数据存储来保存和检索数据。当拥有多线程应用程序时,我知道需要多个
NSManagedObjectContext
对象来正确同步数据。让多个应用程序同时使用相同的核心数据存储会怎么样?在没有冲突、锁等风险的情况下,这可能吗?我如何保证一致性?守护进程应该总是在查看器启动时启动。通过简单地遍历所有打开的进程并检查守护进程的包标识符是否列出,我实现了这一点。如果不是,则使用
NSWorkspace
或launchApplication
启动守护进程。这很好。现在,当用户退出守护进程时,查看器也应该停止。查看器收到守护进程停止通知的最佳方式是什么?我可以定期检查活动进程,并在守护进程消失时退出查看器,但这听起来有点奇怪。我宁愿选择在观看者即将关闭时发送的某种通知。但是由于这个通知应该在应用程序之间发送和捕获,我不知道哪个简单的通知服务可用。任何想法吗?应用程序是沙盒的,因为它将在Mac App Store上分发。启动应用程序与
NSWorkspace
的launchApplication
导致目标应用程序运行在相同的沙盒环境作为源,我认为这根本不是一个问题,因为在同一沙盒中运行两个应用程序感觉更好,可能是。但是想象一下这个场景:在登录时自动启动守护进程(使用SMLoginItemSetEnabled
),用户双击Viewer.app。由于守护进程已经在运行(同样,这是通过循环活动进程来检查的),因此不会启动它。现在我们让守护进程和查看器在不同的沙盒中运行,对吧?这会导致任何关于首选项、核心数据存储等方面的问题吗?我想使用
NSUserDefaults
进行基本配置,我可以在守护进程和查看器之间以某种方式交换此数据吗?同样,两个应用程序将有不同的bundle标识符。
提前感谢您的帮助,谢谢!
这个问题没有一个正确的答案,但我是这样处理的:
守护程序和查看器应用程序都使用相同的核心数据存储来保存和检索数据。
因为进程之间不支持共享核心数据存储(据我所知),所以我让守护进程公开一个XPC服务。查看器将使用NSXPCConnection
通过守护进程访问数据,而不是打开核心数据存储本身。
假设查看器永远不会在没有守护进程的情况下运行,它可以使用SMLoginItemSetEnabled
,就像您在问题中提到的那样,为守护进程注册一个mach服务,然后连接到该服务。
在Apple的网站上有一个示例代码,详细介绍了设置的细节(摘要:守护进程需要在App.app/Contents/Library/LoginItems/daemon.bundle.id.app
),您可能还想阅读这篇讨论沙盒强加的一些额外要求的博客文章(摘要:使extra确保您的Team ID在守护进程的bundle标识符中)。
守护进程应该总是在查看器启动时启动。
所有设置:一旦您在SMLoginItemSetEnabled
中注册了守护进程,launchd将在查看器连接到它的XPC服务时启动它(如果必要的话)。
现在当用户退出守护进程时,查看器也应该停止。
查看器可以使用NSXPCConnection
来查找守护进程何时退出。守护进程还可以在退出前使用SMLoginItemSetEnabled
取消注册,这样它就不会重新启动。
我想使用
NSUserDefaults
进行基本配置,我可以在守护进程和查看器之间以某种方式交换这些数据吗?同样,两个应用程序将有不同的bundle标识符。
使用套件:
// 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来观察共享密钥的更改。