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
相关产品推荐
相关产品推荐

