繁体   English   中英

Firestore:使用SnapshotListener调用异步函数,并在DispatchGroup循环中导致崩溃

[英]Firestore: Calls with of async function with SnapshotListener and in a cycle with DispatchGroup cause a crash

使用FireStore snapshotListener时使用DispatchGroup(如此处所推荐的 )有一个问题

在我的例子中,我有两个功能。 第一个是由ViewController调用的,应该返回要在View中显示的对象数组。 第二个是从每个数组成员的FireStore获取子对象的函数。 它们都必须异步执行。 第二个应该在循环中调用。

所以我使用DispatchGroup等待第二个函数的所有执行完成以调用UI更新。 这是我的代码(请参阅注释部分):

/// Async function returns all tables with active sessions (if any)
class func getTablesWithActiveSessionsAsync(completion: @escaping ([Table], Error?) -> Void) {

    let tablesCollection = userData
        .collection("Tables")
        .order(by: "name", descending: false)

    tablesCollection.addSnapshotListener { (snapshot, error) in
        var tables = [Table]()

        if let error = error {
            completion (tables, error)
        }

        if let snapshot = snapshot {
            for document in snapshot.documents {
                let data = document.data()
                let firebaseID = document.documentID
                let tableName = data["name"] as! String
                let tableCapacity = data["capacity"] as! Int16

                let table = Table(firebaseID: firebaseID, tableName: tableName, tableCapacity: tableCapacity)
                tables.append(table)
            }
        }

        // Get active sessions for each table.
        // Run completion only when the last one is processed.
        let dispatchGroup = DispatchGroup()

        for table in tables {
            dispatchGroup.enter()
            DBQuery.getActiveTableSessionAsync(forTable: table, completion: { (tableSession, error) in
                if let error = error {
                    completion([], error)
                    return
                }
                table.tableSession = tableSession
                dispatchGroup.leave()
            })
        }
        dispatchGroup.notify(queue: DispatchQueue.main) {
            completion(tables, nil)
        }
    }
}

/// Async function returns table session for table or nil if no active session is opened.
class func getActiveTableSessionAsync (forTable table: Table, completion: @escaping (TableSession?, Error?) -> Void) {

    let tableSessionCollection = userData
        .collection("Tables")
        .document(table.firebaseID!)
        .collection("ActiveSessions")

    tableSessionCollection.addSnapshotListener { (snapshot, error) in
        if let error = error {
            completion(nil, error)
            return
        }
        if let snapshot = snapshot {
            guard snapshot.documents.count != 0 else { completion(nil, error); return }

        // some other code

        }
        completion(nil,nil)
    }
}

一切正常,直到由于在第二个函数中使用snapshotListener而更改快照的时刻。 更改数据时,将调用以下闭包:

DBQuery.getActiveTableSessionAsync(forTable: table, completion: { (tableSession, error) in
    if let error = error {
        completion([], error)
        return
    }
    table.tableSession = tableSession
    dispatchGroup.leave()
})

并且它在dispatchGroup.leave()步骤失败,因为此时组是空的。

Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)

所有dispatchGroup.enter()和dispatchGroup.leave()都已在此步骤中完成。 这个闭包是由听众分别调用的。

我试图找到如何检查DispatchGroup是否为空以便不调用leave()方法的方法。 但没有找到任何原生解决方案。 我发现的唯一类似解决方案是在以下答案中 但它看起来太hacky并且不确定是否能正常工作。

有没有办法检查DispatchGroup是否为空? 根据这个答案,没有办法做到这一点。 但可能在过去的两年里发生了一些变化。

有没有其他方法来解决此问题并保持snapshotListener到位?

现在我实现了某种解决方案 - 使用计数器。 我觉得这不是最好的解决方案,但至少目前还在努力。

// Get active sessions for each table.
// Run completion only when the last one is processed.
var counter = tables.count

for table in tables {
    DBQuery.getActiveTableSessionAsync(forTable: table, completion: { (tableSession, error) in
        if let error = error {
            completion([], error)
            return
        }
        table.tableSession = tableSession

        counter = counter - 1
        if (counter <= 0) {
            completion(tables, nil)
        }
    })
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM