【问题标题】:UI freezes for a second when performing .sink operation执行 .sink 操作时,UI 会冻结一秒钟
【发布时间】:2022-11-07 18:48:13
【问题描述】:

我正在使用自定义 PullToRefresh 在用户需要时刷新整个屏幕。 PullToRefresh 执行特定操作,但在执行此操作时会冻结一秒钟。当我从刷新时执行的方法中删除 .sink 时,一切正常。

导致此错误的方法:

private func updateFitnessClasses() {
        if let firstCompany = userCompanies.first {
            updateFitnessClassesInteractor
                .update(companyID: firstCompany.id)
                .combineLatest(fitnessClassesRepository.fitnessClassesSections,
                               fitnessClassesRepository.calendarDayItems)
                .replaceError(with: ((), [], []))
                .sink(receiveValue: { [weak self] _, calendarSections, calendarRows in
                    self?.fitnessClassesSections = calendarSections
                    self?.calendarRows = calendarRows
                })
                .store(in: cancelBag)
        }
    }

其中fitnessClassesRepository.fitnessClassesSectionsfitnessClassesRepository.fitnessClassesSections 是在其他地方构建的AnyPublisher<[CalendarSection], Error>。我在另一种方法中使用这两个发布者,该方法在视图的.onAppear 中触发,并且 UI 不会在那里冻结。
同样UpdateFitnessClassesInteractor's.update() 看起来像这样:

func update(companyID: Int) -> AnyPublisher<Void, Error> {
        fitnessClassCache
            .lastUpdateTimestamp(forCompanyID: companyID)
            .prefix(1)
            .flatMap { self.apiClient.sendRequest(.fitnessClasses(timestamp: $0, companyID: companyID)) }
            .map { $0.data }
            .map { self.fitnessClassCache.updateCache(with: $0) }
            .eraseToAnyPublisher()
    }

有一个 API 调用,然后将新数据保存到数据库中,然后在 fitnessClassesRepository 方法中从中获取。

【问题讨论】:

  • 尝试 .subscribe(on: backgroundQueue) .receive(on: RunLoop.main)
  • @SPatel 说 .subscribe(on: backgroundQueue) 你的意思是 .subscribe(on: DispatchQueue.global(qos: .background))?因为 XCode 告诉我没有“背景队列”之类的东西
  • 是的,使用任何后台队列
  • @SPatel 好的。因此,DispatchQueue.global(qos: .background)) 似乎可以完成这项工作,但是当我拉动以刷新我的应用程序时,由于Terminating app due to uncaught exception 'RLMException', reason: 'Realm accessed from incorrect thread.' 错误而崩溃。因此,当我使用后台线程来使用 PullToRefresh 并且在执行的方法中有 Realm 操作时,如何安排 Realm 在同一个线程中执行所有操作?
  • 您可能需要使您的领域对象线程安全

标签: ios swift swiftui combine pull-to-refresh


【解决方案1】:

事实证明,我的 fitnessClassesRepository 发布者 - fitnessClassesSectionscalendarDayItems - 在主线程上同步执行一些繁重的计算,因此 UI 在执行这些计算时冻结。通过在适当的位置添加.receive(on: DispatchQueue.global()) 并在我的updateFitnessClasses 方法中添加.receive(on: DispatchQueue.main) 将这些计算更改为在后台线程上执行,从而解决了问题。

最后,我的updateFitnessClasses 方法如下所示:

private func updateFitnessClasses() {
        if let firstCompany = userCompanies.first {
            updateFitnessClassesInteractor
                .update(companyID: firstCompany.id)
                .combineLatest(fitnessClassesRepository.fitnessClassesSections,
                               fitnessClassesRepository.calendarDayItems)
                .replaceError(with: ((), [], []))
                .receive(on: DispatchQueue.main)
                .sink(receiveValue: { [weak self] _, calendarSections, calendarRows in
                    self?.fitnessClassesSections = calendarSections
                    self?.calendarRows = calendarRows
                })
                .store(in: cancelBag)
        }
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-31
    • 1970-01-01
    • 1970-01-01
    • 2022-12-10
    • 2017-04-06
    • 1970-01-01
    相关资源
    最近更新 更多