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

Combine框架:CurrentValueSubject转AnyPublisher跨类传递及递归问题排查

问题解决方案

1. 修复递归崩溃问题

递归崩溃的核心原因是UserSettingsClass内部订阅了自身的Publisher,且在回调中调用会触发Publisher发送新值的saveLocalNotifications方法,形成无限循环:Publisher发送值 → sink回调调用saveLocalNotifications → 方法内发送新值 → Publisher再次触发回调 → 循环直至栈溢出。

解决步骤:

  • 直接删除binding()方法及相关订阅代码,这个内部订阅完全无意义——saveLocalNotifications本身就是状态修改的入口,无需监听自身状态再调用自己。
  • 修正saveLocalNotifications方法的逻辑错误,确保CoreData操作与Subject更新的一致性。

修正后的UserSettingsClass代码:

protocol SomeProtocol {
    var isLocalNotificationsEnabledPublisher: AnyPublisher<Bool, Error> { get }
    
    func saveLocalNotifications(_ enabled: Bool) throws
}

final class UserSettingsClass {
    private var repository: CoreDataRepo
    private var isLocalNotificationsEnabledSubject = CurrentValueSubject<Bool, Error>(false)
    private var cancellables = Set<AnyCancellable>()
    
    // 补充CoreData对象获取逻辑示例
    private var userSettings: UserSettingsCoreData? {
        repository.fetchUserSettings()
    }
    
    init(repository: CoreDataRepo) {
        self.repository = repository
        // 初始化时从CoreData加载已有值到Subject
        if let savedValue = userSettings?.isLocalNotificationsEnabled {
            isLocalNotificationsEnabledSubject.send(savedValue)
        }
    }
}

extension UserSettingsClass: SomeProtocol {
    var isLocalNotificationsEnabledPublisher: AnyPublisher<Bool, Error> {
        isLocalNotificationsEnabledSubject.eraseToAnyPublisher()
    }

    func saveLocalNotifications(_ enabled: Bool) throws {
        guard var userSettings = userSettings else {
            throw NSError(domain: "UserSettings", code: -1, userInfo: [NSLocalizedDescriptionKey: "无法获取用户设置"])
        }
        
        // 值未变化时直接返回,避免无效操作
        if userSettings.isLocalNotificationsEnabled == enabled {
            return
        }
        
        // 更新CoreData并保存
        userSettings.isLocalNotificationsEnabled = enabled
        try repository.saveContext()
        
        // 对外发送新状态
        isLocalNotificationsEnabledSubject.send(enabled)
    }
}

2. 修复LocalNotificationsManager的订阅逻辑

当前代码存在两个问题:多次调用requestNotificationAuthorization会创建重复订阅;权限请求未处理已授权的情况,可能重复触发请求。

修正后的LocalNotificationsManager代码:

import Combine
import UserNotifications

final class LocalNotificationsManager {
    private let isLocalNotificationsEnabledPublisher: AnyPublisher<Bool, Error>
    private let center = UNUserNotificationCenter.current()
    private var cancellables = Set<AnyCancellable>()
    
    public init(isLocalNotificationsEnabledPublisher: AnyPublisher<Bool, Error>) {
        self.isLocalNotificationsEnabledPublisher = isLocalNotificationsEnabledPublisher
        // 初始化时完成订阅,无需等待外部调用请求方法
        setupSubscription()
    }
    
    private func setupSubscription() {
        isLocalNotificationsEnabledPublisher
            .removeDuplicates() // 避免重复触发相同状态的逻辑
            .sink(receiveCompletion: { completion in
                if case .failure(let error) = completion {
                    print("通知状态订阅失败: \(error)")
                }
            }, receiveValue: { [weak self] enabled in
                guard enabled else { return }
                self?.requestAuthorizationIfNeeded()
            })
            .store(in: &cancellables)
    }
    
    private func requestAuthorizationIfNeeded() {
        center.getNotificationSettings { [weak self] settings in
            guard settings.authorizationStatus != .authorized else {
                // 已授权,无需重复请求
                return
            }
            
            Task {
                do {
                    let granted = try await self?.center.requestAuthorization(options: [.badge, .sound, .alert])
                    print("通知权限请求结果: \(granted ?? false)")
                } catch {
                    print("请求通知权限失败: \(error)")
                }
            }
        }
    }
}

关键说明

  • UserSettingsClass:作为状态管理类,仅负责维护CoreData与Subject的一致性,对外仅暴露状态修改方法和状态订阅入口,内部无需监听自身状态。
  • LocalNotificationsManager:初始化阶段完成订阅,通过removeDuplicates()过滤重复状态,同时先检查当前权限状态,避免无效的重复授权请求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 13:08:10