You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS/Swift应用CFNotificationCenter屏幕状态事件重复触发问题求助

嘿,我来帮你排查这个屏幕亮灭事件重复触发的问题~ 从你描述的现象(每次循环触发次数递增)来看,最可能的原因是观察者被重复注册了,再结合Darwin通知的特性,咱们一步步来解决:

一、优先排查:观察者是否被多次添加

你这段添加CFNotificationCenterAddObserver的代码,如果是放在applicationDidBecomeActive这类会被多次调用的方法里,每次App从后台回到前台,就会重复注册一次观察者——每多注册一次,同一通知就会多触发一次回调。

解决方法:
给AppDelegate加个标记位,确保观察者只被添加一次:

private var hasRegisteredDisplayObserver = false

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // 只在App启动时注册一次观察者
    if !hasRegisteredDisplayObserver {
        CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(),
                                        Unmanaged.passUnretained(self).toOpaque(),
                                        displayStatusChangedCallback,
                                        "com.apple.springboard.hasBlankedScreen" as CFString,
                                        nil,
                                        .deliverImmediately)
        // 别忘了注册解锁的通知,用来重置状态
        CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(),
                                        Unmanaged.passUnretained(self).toOpaque(),
                                        displayStatusChangedCallback,
                                        "com.apple.springboard.unlockComplete" as CFString,
                                        nil,
                                        .deliverImmediately)
        hasRegisteredDisplayObserver = true
    }
    return true
}

二、添加状态锁,过滤重复通知

有些SpringBoard的通知(比如hasBlankedScreen)可能在系统内部会被多次发送,或者同一状态下重复触发。这时候可以加个状态变量,只在状态变化时执行记录逻辑:

private var isScreenBlanked = false

private func displayStatusChanged(_ lockState: String) {
    print("Darwin notification NAME = \(lockState)")
    
    if lockState == "com.apple.springboard.hasBlankedScreen" {
        // 如果已经是锁屏状态,直接跳过重复通知
        guard !isScreenBlanked else { return }
        isScreenBlanked = true
        
        print("A single Blank Screen")
        let statusString = dbSource()
        statusString.infoString = "blanked screen"
        print("statusString: \(statusString)")
        statusString.save()
    } else if lockState == "com.apple.springboard.unlockComplete" {
        // 解锁时重置状态,避免下次锁屏被过滤
        guard isScreenBlanked else { return }
        isScreenBlanked = false
        
        // 这里可以添加解锁事件的记录逻辑(如果需要的话)
        let statusString = dbSource()
        statusString.infoString = "unlocked screen"
        statusString.save()
    }
}

三、别忘了移除观察者(避免内存问题)

在AppDelegate销毁时,记得移除注册的观察者,防止内存泄漏:

deinit {
    CFNotificationCenterRemoveObserver(CFNotificationCenterGetDarwinNotifyCenter(),
                                       Unmanaged.passUnretained(self).toOpaque(),
                                       "com.apple.springboard.hasBlankedScreen" as CFString,
                                       nil)
    CFNotificationCenterRemoveObserver(CFNotificationCenterGetDarwinNotifyCenter(),
                                       Unmanaged.passUnretained(self).toOpaque(),
                                       "com.apple.springboard.unlockComplete" as CFString,
                                       nil)
}

排查步骤总结

  1. 先确认添加观察者的代码只执行了一次(检查调用时机,加标记位)
  2. 用状态变量过滤同一状态下的重复通知
  3. 配对监听锁屏/解锁通知,保证状态正确切换
  4. 销毁时移除观察者,避免潜在问题

内容的提问来源于stack exchange,提问作者RLW

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:07:41