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

Swift中Kotlin suspendCancellableCoroutine等效实现及优化问询

Swift 封装 NotificationCenter 回调为异步函数的最优方案

核心思路

要把NotificationCenter这类系统回调封装成异步函数,同时在任务取消、错误或正常完成时自动清理监听器,无需依赖额外带deinit的包装类,根据iOS版本兼容需求,有两种主流实现方案。


方案一:iOS 13+ 首选(Combine + AsyncSequence)

利用Combine框架将NotificationCenter监听转为异步序列,代码最简洁,且自动处理监听器生命周期:

import UIKit
import Combine

@MainActor
func waitForLifecycleEvent(_ targetState: UIApplication.State = .active) async throws {
    let notificationName: Notification.Name
    switch targetState {
    case .active:
        notificationName = UIApplication.didBecomeActiveNotification
    case .inactive:
        notificationName = UIApplication.willResignActiveNotification
    case .background:
        notificationName = UIApplication.didEnterBackgroundNotification
    @unknown default:
        throw NSError(domain: "LifecycleEventError", code: -1, userInfo: [NSLocalizedDescriptionKey: "不支持的应用状态"])
    }

    // 监听指定通知,过滤出状态匹配的事件
    let notificationSequence = NotificationCenter.default.notifications(named: notificationName)
        .filter { _ in UIApplication.shared.applicationState == targetState }

    // 等待第一个符合条件的通知
    for await _ in notificationSequence {
        return
    }
}

优势

  • 无需手动管理监听器,Task取消或正常完成时,异步序列会自动终止监听
  • 代码简洁,Combine框架已封装所有生命周期逻辑
  • 自带状态过滤,避免收到通知但实际状态不符的无效触发

方案二:兼容iOS 12及以下(手动管理监听器)

如果需要兼容低版本,结合withCheckedThrowingContinuation和continuation.onTermination统一处理清理逻辑:

import UIKit

@MainActor
func waitForLifecycleEvent(_ targetState: UIApplication.State = .active) async throws {
    let notificationName: Notification.Name
    switch targetState {
    case .active:
        notificationName = UIApplication.didBecomeActiveNotification
    case .inactive:
        notificationName = UIApplication.willResignActiveNotification
    case .background:
        notificationName = UIApplication.didEnterBackgroundNotification
    @unknown default:
        throw NSError(domain: "LifecycleEventError", code: -1, userInfo: [NSLocalizedDescriptionKey: "不支持的应用状态"])
    }

    try await withCheckedThrowingContinuation { continuation in
        // 创建监听器
        let observer = NotificationCenter.default.addObserver(
            forName: notificationName,
            object: nil,
            queue: .main
        ) { _ in
            guard UIApplication.shared.applicationState == targetState else { return }
            // 触发回调后恢复异步任务,同时移除监听器
            NotificationCenter.default.removeObserver(observer)
            continuation.resume()
        }

        // 统一处理任务终止(取消/完成/错误)时的清理
        continuation.onTermination = { _ in
            NotificationCenter.default.removeObserver(observer)
        }
    }
}

关键注意点

  • 回调触发后必须手动移除监听器,此时onTermination不会被调用
  • continuation.onTermination覆盖任务取消、错误抛出等所有终止场景,确保监听器被彻底清理

通用优化点

  1. 状态校验:增加过滤逻辑,确保只有当前应用状态匹配目标状态时才触发回调,避免无效通知
  2. 未知状态处理:用@unknown default捕获未来新增的应用状态,抛出明确错误,避免崩溃
  3. 主线程约束:标注@MainActor确保函数在主线程执行,因为UIApplication状态只能在主线程访问
  4. 错误明确化:自定义错误域和描述,方便调用方快速定位问题

必须注意的问题

  • 任务取消处理:调用方需要捕获CancellationError,因为任务取消时异步函数会抛出该错误
  • 内存泄漏:无论用哪种方案,都要确保监听器在任务终止时被移除,Combine方案或continuation.onTermination能自动处理该逻辑
  • 重复调用:多次调用该函数会创建多个独立监听器,每个任务终止时都会自动清理,无需额外处理

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 23:30:11