Swift中已用主线程更新@Published仍报错,求排查原因
问题分析与解决方案
你遇到的报错核心原因是存在后台线程触发的@Published属性发布操作,即使你已经把posts.append放到了主线程,仍有两个容易被忽略的点:
1. Post模型的后台初始化问题
如果你的Post类遵循ObservableObject且内部包含@Published属性,那么在后台线程解码初始化Post实例时,这些内部@Published属性的初始赋值会在后台线程触发发布,这就违反了“必须在主线程发布更新”的规则——你只处理了数组追加的线程切换,却没处理Post内部属性的发布线程。
解决方法:
优先方案:将Post改为结构体
如果Post不需要作为ObservableObject使用,直接改成结构体(Swift的结构体是值类型,不存在属性发布的线程问题):struct Post: Codable { // 你的Post属性定义 let id: Int let title: String // ...其他属性 }保留ObservableObject的方案
若必须让Post作为ObservableObject,则把整个解码+初始化操作移到主线程执行:URLSession.shared.dataTask(with: url){ data, response, error in guard let result = data else { dGroup.leave() return } DispatchQueue.main.async { let decoder = JSONDecoder() do { let decodedRes = try decoder.decode(Post.self, from: result) self.posts.append(decodedRes) } catch { print("Error decoding data: \(error)") } dGroup.leave() } }.resume()
2. feedPosts的线程安全隐患(潜在问题)
在fetchData的后台线程闭包里直接赋值feedPosts并调用getPosts(),虽然这不是当前报错的直接原因,但feedPosts是在后台线程修改的,后续如果有其他线程访问它可能引发数据竞争。建议把这部分操作移到主线程:
do { let decodedRes = try decoder.decode([Int].self, from: result) DispatchQueue.main.async { self.feedPosts = decodedRes self.getPosts() } } catch { print("Decoding error: \(error)") }
修改后的完整代码示例
import Foundation class NetworkManager: ObservableObject { var feedPosts: [Int] = [] @Published var posts = [Post]() func fetchData(feedType: PostsOrderType) { guard let url = URL(string: "https://hacker-news.firebaseio.com/v0/\(feedType)stories.json") else { return } URLSession.shared.dataTask(with: url) { [weak self] data, response, error in guard let self = self, error == nil, let result = data else { print("Error retrieving data: \(error!)") return } let decoder = JSONDecoder() do { let decodedRes = try decoder.decode([Int].self, from: result) DispatchQueue.main.async { self.feedPosts = decodedRes self.getPosts() } } catch { print("Decoding error: \(error)") } }.resume() } func getPosts() { let dGroup = DispatchGroup() guard feedPosts.count > 15 else { return } let firstFifteenResults = self.feedPosts[0...15] for id in firstFifteenResults { guard let url = URL(string: "https://hacker-news.firebaseio.com/v0/item/\(id).json") else { dGroup.leave() continue } dGroup.enter() URLSession.shared.dataTask(with: url) { [weak self] data, response, error in guard let self = self, let result = data else { dGroup.leave() return } DispatchQueue.main.async { let decoder = JSONDecoder() do { let decodedRes = try decoder.decode(Post.self, from: result) self.posts.append(decodedRes) } catch { print("Error decoding data: \(error)") } dGroup.leave() } }.resume() } } } // 假设Post改为结构体(推荐) struct Post: Codable { let id: Int let title: String // 根据API补充其他属性 }
内容的提问来源于stack exchange,提问作者Simone
相关产品推荐
相关产品推荐

