我有两个函数
add(_:completion:)
和get(query:completion:)
,我认为这可能会导致我的代码出现错误。由于 add(_:completion:)
将 Book
实例附加到 @Published books
属性,因此可能会导致侦听器出现竞争条件。这就是我想象的可能发生的方式:
book
将
add(_:completion:)
add(_:completion:)
函数即将执行它的完成处理程序
add(_:completion:)
完成处理程序在
get(query:completion:)
侦听器块之前执行,则不会有问题,因为对侦听器块的后续调用将通过执行self.books
来覆盖self.books = books
属性。但是,如果之前执行过 get(query:completion:)
侦听器块,则随后执行 add(_:completion:)
完成处理程序将通过执行 self.books
将重复的书附加到 self.books.append(book)
属性的末尾。
//<<< Over here!!!
标记了代码的所有重要部分
class BookRepository: ObservableObject {
@Published private(set) var books = [Book]()
private var listener: ListenerRegistration?
func add(_ book: Book, completion: @escaping (Error?) -> Void) {
let _ = try! db.collection(.books).addDocument(from: book) { error in
if error == nil { self.books.append(book) } //<<< (1) Over here!!! Might get called at the same time as (2)
completion(error)
}
}
func get(query: Query, completion: @escaping (BookError?) -> Void) {
// Storing listener to single property so multiple calls to get(query:completion:) won't create multiple listeners
listener = query.addSnapshotListener { snapshot, error in
var err: BookError?
// Make sure no network errors
guard error != nil else { completion(.networkError); return }
precondition(snapshot != nil, "If there's no error snapshot must exist")
// Convert documents to Book instances
let documents = snapshot!.documents
let books = documents.compactMap { try? $0.data(as: Book.self) }
// Check if all documents were successfully decoded and if I didn't try to access an empty collection
if documents.count != books.count || documents.isEmpty {
err = documents.isEmpty
? .emptyQuery
: .incorrectBookData(self.getBadData(documents, books)) // Retrieves ID's of documents which weren't decoded into Books
}
// Assign books to the @Published property to update UI
self.books = books //<<< (2) Over here!!! Might get called at the same time as (1)
// Call completion handler
completion(err)
}
}
enum BookError: Error {
case networkError
case emptyQuery
case incorrectBookData([String])
}
func getBadData(_ docs: [QueryDocumentSnapshot], _ books: [Book]) -> [String] {
//...
}
}
我假设这可能会导致竞争条件是否正确?执行的顺序是什么?
您可能希望通过确保
add
和
get
方法永远不会同时执行来解决此问题。您可以为此使用某种操作队列。