我们正在为mac编写一个Swift应用程序,其中需要检测计算机的睡眠时间戳。
我们已经发现NSWorkspace.willSleepNotification和NSWorkspace.didWakeNotification不够可靠,因为在很少的情况下,只有在PC醒来并能够处理当前堆栈时才会调用NSWorkspace.willSleepNotification。你还有其他建议吗?
发布于 2019-02-28 23:10:06
在10.14.2查看了AppKit的反汇编之后,我发现NSWorkspace.willSleepNotification是在较低级别的IOKit IORegisterForSystemPower API之上实现的。因此,对于大多数用例来说,使用更高级别的AppKit API就足够了.
一个问题是,AppKit将使用CFRunLoopPerformBlock和kCFRunLoopCommonModes在主运行循环中对通知的发布进行排队。因此,如果主线程被阻塞,或者主运行循环当前正在运行的模式不是常见的模式之一,那么通知的发布将被延迟。然而,电源管理系统给每个进程30秒的时间来响应通知,因此,一个应用程序极不可能在计算机睡觉前就被屏蔽而错过通知。(如果有可能发生这种情况,那么应用程序应该重新设计,以免阻塞主线程。)
发布于 2018-07-16 17:09:10
您可以向系统电源管理器注册回调(请参阅IORegisterForSystemPower)。当系统想要睡觉时(您可以建议它不睡觉),您的回调函数就会被调用,即将进入休眠状态,并且当它再次醒来时。
这个目标-C包装器是十多年前由菲利普·陶氏( Philip )贡献的,至今仍运转良好。这些年来,我可能做了一些轻微的修改,以便为最新的操作系统和Xcode进行编译,但在其他方面没有变化。(注意:原来的网站已经不存在了,所以我就把它粘贴到这里。)用Swift、到Obj代码的桥、或者仅仅使用它作为使用IORegisterForSystemPower并编写自己的回调函数的示例(如果您想要精确的时间)来重写它是很简单的。
要使用Philip的代码,只需调用+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和
//
// PDPowerManagement.m
// Journler
//
// Created by Philip Dow on 3/21/06.
// Copyright 2006 __MyCompanyName__. All rights reserved.
//
#import "PDPowerManagement.h"
@implementation PDPowerManagement
PDPowerManagement *_self;
io_connect_t root_port;
static void callback(void * x,io_service_t y,natural_t messageType,void * messageArgument)
{
switch ( messageType ) {
case kIOMessageSystemWillSleep:
if ( [_self _shouldAllowSleep] ) {
[_self _postPMNotification:PDPowerManagementWillSleep];
IOAllowPowerChange(root_port,(long)messageArgument);
}
else
IOCancelPowerChange(root_port,(long)messageArgument);
break;
case kIOMessageCanSystemSleep:
IOAllowPowerChange(root_port,(long)messageArgument);
break;
case kIOMessageSystemHasPoweredOn:
[_self _postPMNotification:PDPowerManagementPoweredOn];
break;
}
}
#pragma mark -
+ (id)sharedPowerManagement {
static PDPowerManagement *sharedPowerManagement = nil;
if (!sharedPowerManagement)
sharedPowerManagement = [[PDPowerManagement allocWithZone:NULL] init];
return sharedPowerManagement;
}
- (id)init
{
if ( (self=[super init])!=nil )
{
IONotificationPortRef notify;
io_object_t anIterator;
root_port = IORegisterForSystemPower (0,¬ify,callback,&anIterator);
if ( root_port == IO_OBJECT_NULL ) {
NSLog(@"IORegisterForSystemPower failed");
return nil;
}
CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(notify), kCFRunLoopDefaultMode);
_permitSleep = YES;
_self = self;
}
return self;
}
#pragma mark -
- (BOOL) permitSleep { return _permitSleep; }
- (void) setPermitSleep:(BOOL)permitSleep {
_permitSleep = permitSleep;
}
- (id) delegate { return _delegate; }
- (void) setDelegate:(id)delegate {
_delegate = delegate;
}
#pragma mark -
- (void) _postPMNotification:(NSInteger)message {
NSNumber *dictionaryMessage;
NSDictionary *userInfo;
NSNotification *notification;
// Useful for debugging
/*
switch ( message ) {
case PDPowerManagementWillSleep:
NSLog(@"Going to sleep now");
break;
case PDPowerManagementPoweredOn:
NSLog(@"Just had a nice snooze");
break;
}
*/
dictionaryMessage = [NSNumber numberWithInteger:message];
userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
dictionaryMessage, PDPowerManagementMessage, nil];
notification = [NSNotification notificationWithName:PDPowerManagementNotification
object:self userInfo:userInfo];
[[NSNotificationCenter defaultCenter] postNotification:notification];
}
- (BOOL) _shouldAllowSleep {
if ( !_permitSleep )
return NO;
else {
if ( _delegate && [_delegate respondsToSelector:@selector(shouldAllowIdleSleep:)] )
return [_delegate shouldAllowIdleSleep:self];
else
return YES;
}
}
@endhttps://stackoverflow.com/questions/51276280
复制相似问题