我们正在为Mac编写一个Swift应用程序,其中我们需要检测计算机的睡眠时间戳。
我们发现NSWorkspace.willSleepNotification
和NSWorkspace.didWakeNotification
不够可靠,因为在极少数情况下,NSWorkspace.willSleepNotification
仅在PC唤醒并能够处理当前堆栈后才被调用。您还有其他建议吗?
答案 0 :(得分:2)
您可以向系统电源管理器注册回调(请参见IORegisterForSystemPower
)。当系统要进入睡眠状态(可以建议不要),即将进入睡眠状态以及再次唤醒时,将调用回调函数。
此Objective-C包装器由Philip Dow于十年前贡献,至今仍能正常工作。这些年来,我可能已经做了些微修改,因此可以针对最新的OS和Xcode进行编译,但是在其他方面则保持不变。 (注意:原始网站已不存在,因此我将其粘贴在此处。)在Swift中重写,桥接到Obj-C代码或仅以使用{ {1}}并编写自己的回调函数(如果需要确切的时间)。
要使用Philip的代码,只需调用IORegisterForSystemPower
创建单例管理器,然后在通知中心注册观察者以接收+sharedPowerManagement
通知。还有一个委托使用模型。
PDPowerManagementNotification
和
//
// PDPowerManagement.h
// Originally part of the Journler project: http://journler.phildow.net
// Source code available at http://developers.phildow.net
//
// Created by Philip Dow on 3/21/06.
// Licensed under the LGPL: http://www.gnu.org/copyleft/lesser.html
// You may modify and redistribute the code as needed.
// Please keep this original notice intact.
//
// Of course, I would appreciate a mentioning in your app's about box.
// If you make improvements or additions to the code, please let me know.
//
#import <Cocoa/Cocoa.h>
//
// Be sure to include the IOKit Framework in your project
#import <mach/mach_port.h>
#import <mach/mach_interface.h>
#import <mach/mach_init.h>
#import <IOKit/pwr_mgt/IOPMLib.h>
#import <IOKit/IOMessage.h>
//
// Notifications
//
// The PDPowerManagementNotification will be sent to the default notification center
// with the shared instance of PDPowerManagement as the object. To make sure that a shared
// instance is available, call [PDPowerManagement sharedPowerManagement] somewhere in your code.
//
// The notification's user info dictionary will contain the PDPowerManagementMessage key with an
// NSNumber whose int value is either PDPowerManagementWillSleep or PDPowerManagementPoweredOn.
#define PDPowerManagementNotification @"PDPowerManagementNotification"
#define PDPowerManagementMessage @"PDPowerManagementMessage"
#define PDPowerManagementWillSleep 1
#define PDPowerManagementPoweredOn 3
//
// Disallowing Sleep
//
// There are two ways to disallow a power down. Either call setPermitSleep: with NO
// or implement the - (BOOL) shouldAllowIdleSleep:(id)sender delegate method and return NO as needed.
// At initialization _permitSleep is set to YES. With this value, the delegate method is
// always called if the delegate implements it. If _permitSleep is set to NO, the delegate
// method is never called. setPermitSleep: is thus a lazy way of always disallowing sleep.
//
// It must however be noted that it is not possible to cancel a sleep command that the user
// initiates. _permitSleep and the delegate method can only prevent an idle sleep. For
// more information: http://developer.apple.com/qa/qa2004/qa1340.html
@interface PDPowerManagement : NSObject {
BOOL _permitSleep;
id _delegate;
}
+ (id)sharedPowerManagement;
- (BOOL) permitSleep;
- (void) setPermitSleep:(BOOL)permitSleep;
- (id) delegate;
- (void) setDelegate:(id)delegate;
- (void) _postPMNotification:(NSInteger)message;
- (BOOL) _shouldAllowSleep;
@end
//
// Delegation
// You should implement: - (BOOL) shouldAllowIdleSleep:(id)sender
//
// If you set a delegate, before the computer is put to idle sleep the delegate's
// shouldAllowSleep: method will be called. Return NO to disallow the power down,
// return yes to permit it.
@interface NSObject (PDPowerManagementDelegate)
//
// return YES to permit a power down, NO to disallow it
- (BOOL) shouldAllowIdleSleep:(id)sender;
@end
答案 1 :(得分:2)
查看了10.14.2上的AppKit的反汇编之后,我发现NSWorkspace.willSleepNotification
是在较低级别的IOKit IORegisterForSystemPower
API之上实现的。因此,对于大多数用例而言,使用更高级别的AppKit API就足够了。
一个陷阱是,AppKit将使用CFRunLoopPerformBlock
和kCFRunLoopCommonModes
将通知的发布放入主运行循环中。因此,如果您的主线程被阻塞,或者如果主运行循环当前正在非常见模式之一下运行,则通知的发布将被延迟。但是,电源管理系统会给每个进程30秒钟来响应通知,因此应用程序被阻塞的可能性很小,以至于它可能在计算机进入睡眠状态之前错过通知。 (如果可能发生 ,则应重新构建该应用程序,以使其始终不阻塞主线程。)