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

Firestore异步查询阻塞:无法在计算属性中获取文章作者

问题分析与解决方案

这个问题我之前也碰到过,核心是主队列死锁导致的,先给你拆解下为什么会出现这种情况,再给几个可行的解决办法:

为什么你的代码会卡住?

你打印出1、2、4后就没动静了,原因是:

  1. 当你访问author计算属性时,代码按顺序执行print(1)、print(2),然后调用Firestore的getDocument——这是个异步方法,会立刻返回,所以接着执行print(4)。
  2. 然后你调用semaphore.wait(),这会阻塞当前线程。如果这个计算属性是在主队列(UI线程)被访问的,那主队列就被卡死了。
  3. Firestore的getDocument完成闭包,默认会在调用它的队列(也就是被你卡死的主队列)上执行,队列都堵死了,闭包根本没机会运行,自然print(3)和print(5)永远不会出现,semaphore.signal()也不会被调用,程序就一直卡在wait()这里。

正确的处理方式

计算属性是同步的,天生不适合处理异步操作(比如Firestore网络请求),你需要换用异步的方式来获取作者信息,下面是两种常用方案:

方案1:用Async/Await(Swift 5.5+,推荐)

如果你的项目支持Swift 5.5及以上,异步等待的写法最简洁,代码可读性也高:

struct Article {
    private let database = Firestore.firestore()
    var authorId: String
    var message: String
    
    // 异步方法获取作者
    func fetchAuthor() async throws -> User {
        let document = try await database.collection("users").document(authorId).getDocument()
        guard let user = User(dictionary: document.data()) else {
            throw NSError(domain: "ArticleError", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to parse user data"])
        }
        return user
    }
    
    // 或者用异步计算属性(需要iOS 15+/macOS 12+)
    var author: User? {
        get async throws {
            let document = try await database.collection("users").document(authorId).getDocument()
            return User(dictionary: document.data())
        }
    }
}

// 使用示例
Task {
    let article = Article(authorId: "user123", message: "Hello World")
    do {
        let author = try await article.fetchAuthor()
        // 拿到作者信息后做后续处理
        print("Fetched author: \(author)")
    } catch {
        print("Error fetching author: \(error)")
    }
}

方案2:用回调闭包(兼容旧版本Swift)

如果还不能用Async/Await,就用传统的回调方式:

struct Article {
    private let database = Firestore.firestore()
    var authorId: String
    var message: String
    
    func getAuthor(completion: @escaping (User?, Error?) -> Void) {
        database.collection("users").document(authorId).getDocument { document, error in
            if let error = error {
                completion(nil, error)
                return
            }
            // 解析用户数据
            let user = document.flatMap { User(dictionary: $0.data()) }
            completion(user, nil)
        }
    }
}

// 使用示例
let article = Article(authorId: "user123", message: "Hello World")
article.getAuthor { author, error in
    if let error = error {
        print("Could not get author: \(error.localizedDescription)")
        return
    }
    guard let author = author else {
        print("No author data found")
        return
    }
    // 处理获取到的作者信息
    print("Author: \(author)")
}

总结

永远不要在主队列用信号量等待异步操作——这几乎一定会导致死锁。异步操作就用异步的方式处理,要么用现代的Async/Await,要么用回调闭包,这才是符合iOS/Swift异步编程模型的正确做法。

内容的提问来源于stack exchange,提问作者user3116871

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:50:46