Firestore异步查询阻塞:无法在计算属性中获取文章作者
问题分析与解决方案
这个问题我之前也碰到过,核心是主队列死锁导致的,先给你拆解下为什么会出现这种情况,再给几个可行的解决办法:
为什么你的代码会卡住?
你打印出1、2、4后就没动静了,原因是:
- 当你访问
author计算属性时,代码按顺序执行print(1)、print(2),然后调用Firestore的getDocument——这是个异步方法,会立刻返回,所以接着执行print(4)。 - 然后你调用
semaphore.wait(),这会阻塞当前线程。如果这个计算属性是在主队列(UI线程)被访问的,那主队列就被卡死了。 - 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
相关产品推荐
相关产品推荐

