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

Fetching data from CloudKit with queries in a loop retrieves wrong data

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

我有一个 Post 记录类型,其 userCKReference,以便跟踪谁是 post 创建者。

我的 post struct 构造函数将 user 作为输入,因此我可以 "mount" 显示 post。

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

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 然后遍历获取的 posts user CKReference 以获取创建post 然后我构造我的 post 以 UICollectionView 形式呈现给用户。

使用这种方法我遇到了问题。

我用10个post做了一个测试,其中post1是由[=27=创建的],post2是由user2创建的,post3user1 创建,post4user2 创建(依此类推...)

10 post 由这两个用户创建,一次一个。

当我遍历 FOR LOOP 来获取用户时,不知何故获得的顺序不正确,它混合了用户顺序。

我希望看到 user1user2user1user2 等...

但是我得到的user1user1user2user1都是混合的并且顺序很奇怪,如果我刷新给定的顺序就会不同来自上一个。

即使我在 fetch 操作中打印 record.recordID 出错,它也与 reference.recordID 不一样。

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

为了进行测试,我创建了一个递归函数,它仅在成功获取前一个用户时才从服务器获取下一个用户,由此我注意到现在可以正确获取用户。

这是我的递归函数:

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)
    }
}

我在 queryCompletionBlock 中调用它来替换之前的 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)

这样我可以保证相应 post 的用户不会出错,但是这样我就遇到了一个巨大的性能障碍。

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

Execution time: 1.15568399429321

递归函数是这样的:

Execution time: 4.56143599748611

如您所见,它几乎增加了 4 倍。

这是我的问题:

什么可能导致 for 循环内的数据损坏?以及如何提高递归函数的性能?

这可能是 CloudKit 中的错误吗?

谢谢。 问候。

编辑

经过一些测试,我对 For loop 做了以下操作:

    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)")

        }
    })
}

如您所见,我尝试在获取之前和获取期间跟踪索引,这些是打印件:

index: 1
recordID: <CKRecordID: 0x6180002382a0; recordName=5FF7D994-7F0D-4F1F-90A2-BB765EB52E27, zoneID=_defaultZone:__defaultOwner__>

index: 2
recordID: <CKRecordID: 0x6180002383c0; recordName=25221185-4E74-4268-B3E9-3EF3AA435A76, zoneID=_defaultZone:__defaultOwner__>

index: 3
recordID: <CKRecordID: 0x618000238200; recordName=5FF7D994-7F0D-4F1F-90A2-BB765EB52E27, zoneID=_defaultZone:__defaultOwner__>

index: 4
recordID: <CKRecordID: 0x60000003b600; recordName=25221185-4E74-4268-B3E9-3EF3AA435A76, zoneID=_defaultZone:__defaultOwner__>

index: 5
recordID: <CKRecordID: 0x600000038420; recordName=5FF7D994-7F0D-4F1F-90A2-BB765EB52E27, zoneID=_defaultZone:__defaultOwner__>

index: 6
recordID: <CKRecordID: 0x600000036640; recordName=25221185-4E74-4268-B3E9-3EF3AA435A76, zoneID=_defaultZone:__defaultOwner__>

index: 7
recordID: <CKRecordID: 0x600000039b60; recordName=5FF7D994-7F0D-4F1F-90A2-BB765EB52E27, zoneID=_defaultZone:__defaultOwner__>

index: 8
recordID: <CKRecordID: 0x60000003b780; recordName=25221185-4E74-4268-B3E9-3EF3AA435A76, zoneID=_defaultZone:__defaultOwner__>

index: 9
recordID: <CKRecordID: 0x60000003b580; recordName=5FF7D994-7F0D-4F1F-90A2-BB765EB52E27, zoneID=_defaultZone:__defaultOwner__>

index inside fetch: 1
recordID inside fetch: <CKRecordID: 0x6180002382a0; recordName=5FF7D994-7F0D-4F1F-90A2-BB765EB52E27, zoneID=_defaultZone:__defaultOwner__>
fetched recordID: Optional(<CKRecordID: 0x6100000369e0; recordName=5FF7D994-7F0D-4F1F-90A2-BB765EB52E27, zoneID=_defaultZone:__defaultOwner__>)

index inside fetch: 4
recordID inside fetch: <CKRecordID: 0x60000003b600; recordName=25221185-4E74-4268-B3E9-3EF3AA435A76, zoneID=_defaultZone:__defaultOwner__>
fetched recordID: Optional(<CKRecordID: 0x60000003c640; recordName=25221185-4E74-4268-B3E9-3EF3AA435A76, zoneID=_defaultZone:__defaultOwner__>)

index inside fetch: 3
recordID inside fetch: <CKRecordID: 0x618000238200; recordName=5FF7D994-7F0D-4F1F-90A2-BB765EB52E27, zoneID=_defaultZone:__defaultOwner__>
fetched recordID: Optional(<CKRecordID: 0x610000035fa0; recordName=5FF7D994-7F0D-4F1F-90A2-BB765EB52E27, zoneID=_defaultZone:__defaultOwner__>)

index inside fetch: 2
recordID inside fetch: <CKRecordID: 0x6180002383c0; recordName=25221185-4E74-4268-B3E9-3EF3AA435A76, zoneID=_defaultZone:__defaultOwner__>
fetched recordID: Optional(<CKRecordID: 0x610000038f80; recordName=25221185-4E74-4268-B3E9-3EF3AA435A76, zoneID=_defaultZone:__defaultOwner__>)

index inside fetch: 6
recordID inside fetch: <CKRecordID: 0x600000036640; recordName=25221185-4E74-4268-B3E9-3EF3AA435A76, zoneID=_defaultZone:__defaultOwner__>
fetched recordID: Optional(<CKRecordID: 0x60000003c640; recordName=25221185-4E74-4268-B3E9-3EF3AA435A76, zoneID=_defaultZone:__defaultOwner__>)

index inside fetch: 0
recordID inside fetch: <CKRecordID: 0x608000037b20; recordName=25221185-4E74-4268-B3E9-3EF3AA435A76, zoneID=_defaultZone:__defaultOwner__>
fetched recordID: Optional(<CKRecordID: 0x618000238560; recordName=25221185-4E74-4268-B3E9-3EF3AA435A76, zoneID=_defaultZone:__defaultOwner__>)

index inside fetch: 7
recordID inside fetch: <CKRecordID: 0x600000039b60; recordName=5FF7D994-7F0D-4F1F-90A2-BB765EB52E27, zoneID=_defaultZone:__defaultOwner__>
fetched recordID: Optional(<CKRecordID: 0x60800003abe0; recordName=5FF7D994-7F0D-4F1F-90A2-BB765EB52E27, zoneID=_defaultZone:__defaultOwner__>)

index inside fetch: 9
recordID inside fetch: <CKRecordID: 0x60000003b580; recordName=5FF7D994-7F0D-4F1F-90A2-BB765EB52E27, zoneID=_defaultZone:__defaultOwner__>
fetched recordID: Optional(<CKRecordID: 0x60000003c780; recordName=5FF7D994-7F0D-4F1F-90A2-BB765EB52E27, zoneID=_defaultZone:__defaultOwner__>)

index inside fetch: 8
recordID inside fetch: <CKRecordID: 0x60000003b780; recordName=25221185-4E74-4268-B3E9-3EF3AA435A76, zoneID=_defaultZone:__defaultOwner__>
fetched recordID: Optional(<CKRecordID: 0x60000003c7e0; recordName=25221185-4E74-4268-B3E9-3EF3AA435A76, zoneID=_defaultZone:__defaultOwner__>)

index inside fetch: 5
recordID inside fetch: <CKRecordID: 0x600000038420; recordName=5FF7D994-7F0D-4F1F-90A2-BB765EB52E27, zoneID=_defaultZone:__defaultOwner__>
fetched recordID: Optional(<CKRecordID: 0x610000039680; recordName=5FF7D994-7F0D-4F1F-90A2-BB765EB52E27, zoneID=_defaultZone:__defaultOwner__>)
Execution time: 1.74884396791458

在提取之前索引是正确的,但由于循环比提取快得多,提取中的索引乱序所以我破坏了我的数组。

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

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

现在你可以做些什么:

您的一个选择是在调用后对结果进行排序

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