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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 00:25:24