在主线程上使用 @Published 值?

Posted

技术标签:

【中文标题】在主线程上使用 @Published 值?【英文标题】:Consume @Published values on the main thread? 【发布时间】:2019-11-26 22:39:31 【问题描述】:

有没有办法指定 count 只应该在 主线程上发布?我看到过有关使用 receive(on:) 设置发布者的文档,但在这种情况下,@Publisher 包装器隐藏了该逻辑。

import SwiftUI
import Combine

class MyCounter: ObservableObject 
  @Published var count = 0

  public static let shared = MyCounter()
  
  private init()  


struct ContentView: View 
    @ObservedObject var state = MyCounter.shared
    var body: some View 
        return VStack 
            Text("Current count: \(state.count)")
            Button(action: increment) 
                HStack(alignment: .center) 
                    Text("Increment")
                        .foregroundColor(Color.white)
                        .bold()
                
            
        
    
    
    private func increment() 
        NetworkUtils.count()
    


public class NetworkUtils 

    public static func count() 
        guard let url = URL.parse("https://www.example.com/counter") else 
            return
        
        
        var request = URLRequest(url: url)
        request.httpMethod = "GET"
        
        let task = URLSession.shared.dataTask(with: request)  data, response, error in
            if let response = response as? HTTPURLResponse 
                let statusCode = response.statusCode
                if statusCode >= 200 && statusCode < 300 
                    do 
                        guard let responseData = data else 
                            return
                        
                        
                        guard let json = try JSONSerialization.jsonObject(with: responseData, options: []) as? [String: Any] else 
                            return
                        
                        
                        if let newCount = json["new_count"] as? Int
                            MyCounter.shared.count = newCount
                        
                     catch  
                        print("Caught error")
                    
                
             
        
        task.resume()
       

从我更新的示例中可以看出,这是一个简单的 SwiftUI 视图,它有一个按钮,单击该按钮会进行网络调用。网络调用是异步的。当网络调用返回时,ObservableObject MyCounter 在后台线程上更新。我想知道是否有办法让ObservableObject 在主线程上发布更改。我现在知道的唯一方法是将更新逻辑包装在网络调用闭包中,如下所示:

DispatchQueue.main.async 
    MyCounter.shared.count = newCount

【问题讨论】:

显示你设置MyCounter.count的代码。 我是从网络调用的闭包中设置值,这当然不会在主线程上。 好的,将该代码添加到您的问题中。我们无法帮助您修复您未包含的内容。 【参考方案1】:

您可以使用URLSession.shared.dataTaskPublisher(for: request) (docs) 而不是使用URLSession.shared.dataTask(with: request),这将允许您创建一个组合管道。然后,您可以将 receive(on:) 运算符链接为管道的一部分。

URLSession.shared.dataTaskPublisher(for: request)
  .map  response in ... 
  ...
  .receive(on: RunLoop.main)
  ...

还可以查看heckj's examples,我发现它们非常有用。

【讨论】:

我认为这是最好的方法【参考方案2】:

如果您尝试从后台线程设置标记为 @Published 的值,您将看到此错误:

不允许从后台线程发布更改;确保 从主线程发布值(通过接收等操作符

因此,您必须确保在主线程上设置值的任何位置,这些值将始终在主线程上发布。

【讨论】:

对。这就是我看到的错误,我想知道是否有办法配置使用 @Published 包装器自动将更新移动到主线程时使用的 Publisher。现在,我只是通过使用 DispatchQueue.main.async 切换到主线程来更新我使用的那些字段。

以上是关于在主线程上使用 @Published 值?的主要内容,如果未能解决你的问题,请参考以下文章

GCD主队列是不是必须在主线程上执行?

在主线程上调用方法?

多线程学习 ThreadLocal的使用。

如何在主线程上安全地使用[NSTask waitUntilExit]?

如何让工作线程在主 UI 线程上执行回调?

iOS - 确保在主线程上执行[重复]