在OSX中禁用特定进程的定时器合并

13

我有一个后台应用程序,每1.5秒钟需要向另一个进程发送保持活动状态的信号。 在OSX 10.7和10.8中一切正常,但在OSX 10.9下会错过很多保活通知,有时达到3个。通常在前3或4分钟内一切正常,然后问题开始发生。

经过进一步检查,似乎OSX Mavericks的“定时器协同”功能会决定将请求的1.5秒延长至最多4.0秒。

有没有办法指示NSThread不进行协同?或者至少指示允许的最大协同变化?

参考以下代码:

+(void)keepAliveThread
{
    @autoreleasepool {
        void (^keepAlive)() = ^ (){
            // (snipped!) do something...
        };
        dispatch_queue_t mainQueue = dispatch_get_main_queue();
        while( [NSThread currentThread].isCancelled == NO )
        {
            @autoreleasepool {
                dispatch_async(mainQueue, keepAlive);
                [NSThread sleepForTimeInterval:1.5];
            }
        }
    }
}

相信用户可以针对每个应用程序关闭它,但这可能不是您真正想要的解决方案。 - Kevin
1
为什么保持连接工作需要在主线程上完成? - trojanfoe
1
无论工作是在主线程还是次要线程上进行,问题都会出现。如果所有UI元素都隐藏或关闭,则App Nap将针对所有没有可见UI元素的应用程序启动。如果所有UI元素都隐藏或关闭,则应用程序将看到其计时器被App Nap减慢。 - ekscrypto
2个回答

8

在苹果开发者论坛上,一位用户推荐我观看WWDC 2013的视频“通过App Nap提高功率效率”,在其中我找到了解决方案:

static dispatch_source_t _keepAliveTimer;

+(void)enable
{
    _keepAliveTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, DISPATCH_TIMER_STRICT, dispatch_get_main_queue());
    dispatch_source_set_event_handler(_keepAliveTimer, ^{
        // do something
    });
    dispatch_source_set_timer(_keepAliveTimer, dispatch_time(DISPATCH_TIME_NOW, 1.5 * NSEC_PER_SEC), 1.5 * NSEC_PER_SEC, 0.5 * NSEC_PER_SEC);
    dispatch_resume(_keepAliveTimer);
}

这段代码将在1.5秒后触发一个定时器(误差在0.5秒左右),无论LSUIElement状态如何,都会防止App Nap在该定时器时间内启动。

6
听起来你正在运行后台应用程序,但没有设置适当的.plist键。
如果你正在使用后台应用程序,则必须在应用程序的plist中将“应用程序是代理(UIElement)”(LSUIElement)选项设置为YES或将“应用程序仅为后台”(LSBackgroundOnly)选项设置为yes,否则它将受到App Nap的影响,这就是你在这种情况下遇到的问题。我不会期望计时器合并会产生计时器间隔的巨大差距。
LSUIElement适用于可能只有浮动窗口或状态栏项的应用程序。它们不会得到菜单栏,并且不会得到dock图标。
App Nap旨在影响前端用户应用程序。根据文档,有4件事会导致应用程序进入App nap:
  • 它不可见——如果应用程序的所有窗口都被其他窗口隐藏或最小化到隐藏的dock中,并且该应用程序不在前台
  • 它不可听
  • 它没有明确禁用自动终止
  • 它没有采取任何电源管理断言
如果你想防止用户应用程序遇到App nap,那么你将不得不遵循一种支持的机制,使其中一个这些状态不活跃。
如果你使用IOPmlib.h API,你可以为你的应用程序创建一个电源管理断言,以防止App nap。
或者,你可以使用以下方法禁用自动终止:
[[NSProcessInfo processInfo] disableAutomaticTermination:@"Good Reason"];

再次启用自动终止的方法:

[[NSProcessInfo processInfo] enableAutomaticTermination:@"Good Reason"];

但这通常用于需要在应用程序被认为是“good to stop”之前完成的代码,例如写出偏好设置。

苹果在文档中确实指出,如果您的应用程序遇到与应用程序休眠相关的问题,则应提交一个故障报告,以确定它是否是其实现中的错误。


我认为使用固定的sleepInterval的NSThread比NSTimer具有更严格和固定的行为,这就是我选择这种方式的原因。 - ekscrypto
尝试使用NSTimer,在后台运行30分钟后,NSTimer间隔被设置为1.5秒,允许1.0秒,但超过12秒没有触发。 - ekscrypto
从问题来看,这似乎是一个LSUIElement或LSBackground应用程序,它们根本不受App Nap的影响。 - jscs
3
您可以使用“taskinfo”命令检查应用程序是否正在应用App Nap。如果正在使用,您可以使用新的NSProcessInfo API(不是自动终止,而是10.9中的新API),以及dispatch计时器的新的“precise”标志。请注意,sleep()与NSTimer和其他计时器类似,同样受到App Nap的影响。 - Catfish_Man
我正在进行这项工作的应用程序实际上根据是否需要显示UI元素(控制面板)在LSUIElement YES和NO之间进行切换。 - ekscrypto
为了更详细地介绍这个应用程序,它通常位于状态栏中,在此期间作为LSUIELement运行。当特定事件到达时,该应用程序会切换到常规应用程序,因此其图标会显示在Dock中,并根据需要显示警报或控制面板。 - ekscrypto

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接