Mac App 睡眠和唤醒的通知、实现‘App防睡眠’

1,029 阅读4分钟

实现Mac端App的功能:a.睡眠和唤醒的通知、b.实现‘App防睡眠’ (创建桥接头文件)

参考: Registering and unregistering for sleep and wake notifications

用IOKit阻止Mac进入睡眠模式

Developer 开发者文档Documentation Archive


Cocoa可以用来接收睡眠唤醒通知,而I/O Kit还可以防止延迟 闲置睡眠(idle sleep)。然而,即使有I/O Kit,也不可能防止强制睡眠(forced sleep),只能延迟它。

注意: Mac OS X两种不同的情况下睡眠强制(forced)和闲置(idle)。

  • 当用户采取某种直接行动让机器进入睡眠状态时,就会发生强制睡眠合上笔记本电脑的盖子或从苹果顶部菜单栏选择'睡眠'都会导致强制睡眠。该系统还将在某些条件下诱导强制睡眠,例如热紧急情况电池电量不足
  • 闲置睡眠是闲置一段时间发生的,是机器闲置的一段时间内配置的节能系统首选项

本文只实现了OC代码的睡眠/唤醒通知使用、‘App防睡眠’方法! 关于相应C语言实现,具体可以参考《Registering and unregistering for sleep and wake notifications》~

注册/注销 睡眠和唤醒的通知

-(void)OC_registerNotications { //OC-注册通知
    //These notifications are filed on NSWorkspace's notification center, not the default
        // notification center. You will not receive sleep/wake notifications if you file
        //with the default notification center.
    [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(receiveSleepNote:) name:NSWorkspaceWillSleepNotification object:NULL];
    
    [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(receiveWakeNote:) name:NSWorkspaceDidWakeNotification object:NULL];
}
-(void)OC_removeNotifications { //OC-移除通知
    [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self name:NSWorkspaceWillSleepNotification object:NULL];
    [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self name:NSWorkspaceDidWakeNotification object:NULL];
}
-(void)receiveSleepNote:(NSNotification *)note {//睡眠的响应
    NSLog(@"receiveSleepNote: %@", [note name]);
}
-(void)receiveWakeNote:(NSNotification *)note { //唤醒的响应
    NSLog(@"receiveWakeNote: %@", [note name]);
}

实现‘App防睡眠’功能 :打开Mac App时,不进入睡眠模式!

头文件:#import <IOKit/pwr_mgt/IOPMLib.h> 实现代码如下:

// kIOPMAssertionTypeNoDisplaySleep prevents display sleep,
// kIOPMAssertionTypeNoIdleSleep prevents idle sleep
 
//reasonForActivity is a descriptive string used by the system whenever it needs
//  to tell the user why the system is not sleeping. For example,
//  "Mail Compacting Mailboxes" would be a useful string.
 
//  NOTE: IOPMAssertionCreateWithName limits the string to 128 characters.
CFStringRef reasonForActivity = CFSTR("Describle Activity Type");

IOPMAssertionID assertionID;
IOReturn success = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, reasonForActivity, &assertionID);
if (success == kIOReturnSuccess) {
    //Add the work you need to do without
    //  the system sleeping here.
    
    success = IOPMAssertionRelease(assertionID);
    //The system will be able to sleep again.
}

1.新旧IPA

  • 废弃的IPA:IOPMAssertionCreate(<#CFStringRef AssertionType#>, <#IOPMAssertionLevel AssertionLevel#>, <#IOPMAssertionID *AssertionID#>) 使用提示信息:'IOPMAssertionCreate' is deprecated: first deprecated in macOS 10.6

  • 新的IPA:IOPMAssertionCreateWithName是Mac OS X 10.6雪豹中可用的新API。 IOPMAssertionCreateWithName允许应用程序返回一个简短的字符串给用户,解释为什么该应用程序阻止了睡眠。

2.关于AssertionType参数 —— kIOPMAssertionType... 用kIOPMAssertionTypeNoDisplaySleep 防止显示器睡眠(会防止系统睡眠); 用kIOPMAssertionTypeNoIdleSleep 防止系统睡眠(显示器会睡眠)。

测试结果是:

kIOPMAssertionTypeNoDisplaySleep 既能防止显示器睡眠又能防止系统睡眠,就像放视频或做幻灯片一样。


封装 (根据个人需求)

两个方法集成到一个OC类(MacSleepWakeObject)里面:均使用类方法执行相关操作~

(😒 嫌弃在Swift中找相应IPA — 防止系统睡眠使用I/O Kit时有使用C语言的方法,翻译成Swift有点麻烦! 所以还是用OC类来进行桥接~)

.h文件:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface MacSleepWakeObject : NSObject

+(void)preventSystemSleep;//防止系统休眠

+(void)OC_registerNotications;//OC-注册通知
+(void)OC_removeNotifications;//OC-移除通知

@end

NS_ASSUME_NONNULL_END

.m文件:

#import "MacSleepWakeObject.h"

#import <IOKit/pwr_mgt/IOPMLib.h>//防止休眠

@import Cocoa;//才可以 注册、移除通知


@implementation MacSleepWakeObject

//防止系统休眠
+(void)preventSystemSleep {
    // kIOPMAssertionTypeNoDisplaySleep prevents display sleep,
    // kIOPMAssertionTypeNoIdleSleep prevents idle sleep
     
    //reasonForActivity is a descriptive string used by the system whenever it needs
    //  to tell the user why the system is not sleeping. For example,
    //  "Mail Compacting Mailboxes" would be a useful string.
     
    //  NOTE: IOPMAssertionCreateWithName limits the string to 128 characters.
    CFStringRef reasonForActivity = CFSTR("Describle Activity Type");
    
    IOPMAssertionID assertionID;
    IOReturn success = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, reasonForActivity, &assertionID);
    if (success == kIOReturnSuccess) {
        //Add the work you need to do without
        //  the system sleeping here.
        
        success = IOPMAssertionRelease(assertionID);
        //The system will be able to sleep again.
    }
}

+(void)OC_registerNotications { //OC-注册通知
    //These notifications are filed on NSWorkspace's notification center, not the default
        // notification center. You will not receive sleep/wake notifications if you file
        //with the default notification center.
    [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(receiveSleepNote:) name:NSWorkspaceWillSleepNotification object:NULL];
    
    [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(receiveWakeNote:) name:NSWorkspaceDidWakeNotification object:NULL];
}
+(void)OC_removeNotifications { //OC-移除通知
    [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self name:NSWorkspaceWillSleepNotification object:NULL];
    [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self name:NSWorkspaceDidWakeNotification object:NULL];
}
+(void)receiveSleepNote:(NSNotification *)note {//睡眠的响应
    NSLog(@"receiveSleepNote: %@", [note name]);
}
+(void)receiveWakeNote:(NSNotification *)note { //唤醒的响应
    NSLog(@"receiveWakeNote: %@", [note name]);
}


@end

手动创建 桥接头文件

创建新文件时选择'Header File'项创建一个头文件,以'-Brigding-Header.h'结尾的格式命名(命名为"SwiftAndOC-Brigding-Header.h")!

选择'Header File'项

将其命名为"SwiftAndOC-Brigding-Header.h"

在该工程中找到该TARGET,在'Build Settings'中找到'Objective-C Bridging Header'项!并输入“${SRCROOT}/MacSleepWake/SwiftAndOC-Brigding-Header.h”——创建好桥接关系

找到'Objective-C Bridging Header'项

输入“${SRCROOT}/MacSleepWake/SwiftAndOC-Brigding-Header.h”

创建好桥接关系~

创建好桥接关系之后,将该类(MacSleepWakeObject)引入桥接头文件("SwiftAndOC-Brigding-Header.h")中 — #import "MacSleepWakeObject.h"!就可以在Swift代码中使用该类(MacSleepWakeObject)的方法了:

override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view.
    
    MacSleepWakeObject .preventSystemSleep()//防止系统休眠
    
    MacSleepWakeObject .oc_registerNotications()//OC-注册通知
    //DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 30.0) {//延时30s
    //MacSleepWakeObject .oc_removeNotifications()//OC-移除通知
    //}
}


以上便是关于“实现Mac端App的功能(a.睡眠和唤醒的通知、b.实现‘App防睡眠’)”的讨论~

关于更多macOS系统状态相关讨论,请参考《macOS系统的状态切换及其响应方法》!

关于NSApp对应的App状态讨论,请参考《NSApp — App状态及其通知》!

goyohol's essay