从循环中的查询中获取CloudKit中的数据会检索错误的数据

时间:2017-08-17 08:58:59

标签: ios recursion swift3 cloudkit

我有一个使用CloudKit作为后端的应用。

我的Post记录类型userCKReference,以便跟踪帖子创建者的身份。

我的post struct构造函数将user作为输入,因此我可以“挂载”要显示的帖子。

当我查询服务器以检索10个帖子时,我使用以下代码:

var postRecords = [CKRecord]()
var userRecords = [User]()
let qop = CKQueryOperation(query: q)
qop.resultsLimit = 10

qop.recordFetchedBlock = {(record: CKRecord) in
    postRecords.append(record)
    let userReference = record["user"] as! CKReference
    self.userCKReferences.append(userReference)
}

qop.queryCompletionBlock = {(cursor, err) in
    if err != nil {
        print("queryCompletionBlock error:", err ?? "")
        return
    }

    let start = Date()

    for reference in self.userCKReferences {

        database.fetch(withRecordID: reference.recordID, completionHandler: { (tmpUserCKR, err) in

            if err != nil {
                print("Error:", err ?? "")
                return
            }


            let tmpUser = User() // custom initializations here
            userRecords.append(tmpUser)

            if userRecords.count == postRecords.count {
                self.assemblePosts(posts: postRecords, users: userRecords, previousCounter: previousPostCounter, firsTime: firstTime)
                let finish = Date()
                let executionTime = finish.timeIntervalSince(start)
                print("Execution time: \(executionTime)")

            }
            })
    }
}
database.add(qop)

正如您所看到的,我获取了post,然后我遍历提取的帖子user CKReference,以便获取创建帖子的相应用户,然后构建我的帖子向用户显示UICollectionView

通过这种方法,我遇到了一个问题。

我对10个帖子进行了测试,其中post1user1创建,post2user2创建,post3由{创建{1}},user1post4创建(依此类推......)

这两个用户创建的10个帖子,一次一个。

当我在user2中迭代以获取用户时,不知何故获得的订单不是正确的,它会混合用户订单。

我希望看到FOR LOOPuser1user2user1等...

但是我给了user2user1user1user2所有混合和奇数顺序,如果我刷新给定的顺序将不同于前一个。

即使我在user1操作中出现record.recordID时出错,它也应该与fetch不同。

我可能会发生这种情况,因为查询是异步进行的,输入可能是错误的(在查询完成之前再进行一次迭代)并且要获取的reference.recordID是错误的,但是为了发生这种情况我不会我认为没有提取10个帖子,但我得到了它们 - 帖子被正确提取但用户却没有。

为了进行测试,我创建了一个递归函数,该函数仅在成功获取上一个用户时从服务器获取下一个用户,并且我注意到现在用户已正确获取。

这是我的递归函数:

recordID

我在var refPosition: Int = 0 var userRecords = [User]() func recursiveFetch(list: [CKReference], completion: ((Bool) -> Swift.Void)? = nil) { if refPosition == list.count { // All data fetched completion?(true) refPosition = 0 userRecords = [User]() return } let database = CKContainer.default().publicCloudDatabase let id = list[refPosition].recordID database.fetch(withRecordID: id) { (record, err) in if err != nil { print("Failed to fetch user with error:", err ?? "") return } let tmpUser = User() // with some initializations self.userRecords.append(tmpUser) self.refPosition += 1 self.recursiveFetch(list: list, completion: completion) } } 内部调用它取代之前的for循环,如下所示:

queryCompletionBlock

通过这种方式,我保证相应帖子的用户不会出错,但有了这个,我就有了巨大的性能块。

使用常规for循环,我的函数执行时间如下:

  

执行时间:1.15568399429321

使用递归函数,它是这样的:

  

执行时间:4.56143599748611

正如您所注意到的那样,它几乎是其中的4倍。

这是我的问题:

在for循环中可能导致数据损坏的原因是什么?如何提高递归函数的性能?

这可能是var postRecords = [CKRecord]() let qop = CKQueryOperation(query: q) qop.resultsLimit = 10 qop.recordFetchedBlock = {(record: CKRecord) in postRecords.append(record) let userReference = record["user"] as! CKReference self.userCKReferences.append(userReference) } qop.queryCompletionBlock = {(cursor, err) in if err != nil { print("queryCompletionBlock error:", err ?? "") return } let start = Date() self.recursiveFetch(list: self.userCKReferences, completion: { (success) in if self.userRecords.count == postRecords.count { self.assemblePosts(posts: postRecords, users: self.userRecords) let finish = Date() let executionTime = finish.timeIntervalSince(start) print("Execution time: \(executionTime)") } }) } database.add(qop) 中的错误吗?

谢谢。 问候。

修改

经过一些测试后,我已完成以下CloudKit

For loop

正如你所看到的,我试图在fetch之前和之内跟踪索引,这些是打印:

    for (i,reference) in self.userCKReferences.enumerated() {
    print("\nindex:", i)
    print("recordID:", reference.recordID)

    database.fetch(withRecordID: reference.recordID, completionHandler: { (tmpUserCKR, err) in

        print("\nindex inside fetch:", i)
        print("recordID inside fetch:", reference.recordID)
        print("fetched recordID:", tmpUserCKR?.recordID)

        if err != nil {
            print("Error in reference loop:", err ?? "")
            return
        }


        let tmpUser = User(/* with custom inits*/)
        userRecords.append(tmpUser)

        if userRecords.count == postRecords.count {
            self.assemblePosts(posts: postRecords, users: userRecords, previousCounter: previousPostCounter, firsTime: firstTime)
            let finish = Date()
            let executionTime = finish.timeIntervalSince(start)
            print("Execution time: \(executionTime)")

        }
    })
}

在获取索引之前,索引是正确的,但由于循环比fetch更快,因此fetch中的索引会出现故障,因此我破坏了我的数组。

克服这个问题的最佳方法是什么?我应该在完成后对对象进行排序吗?如果是这样,最好的方法是什么?

1 个答案:

答案 0 :(得分:0)

由于您正在使用异步调用的用户,这就是用户的顺序不同的原因,第一次用户引用的请求有时可能比user2花费更多的时间,因此user2被添加到user1之前的数组中,

现在你可以做些什么:

您可以选择在调用后对结果进行排序

对数组进行排序,您可以使用sortdescriptor