使用循环中的查询从 CloudKit 中获取数据会检索到错误的数据
Fetching data from CloudKit with queries in a loop retrieves wrong data
我有一个使用 CloudKit
作为后端的应用程序。
我有一个 Post
记录类型,其 user
为 CKReference
,以便跟踪谁是 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
创建的,post3
由 user1
创建,post4
由 user2
创建(依此类推...)
10 post 由这两个用户创建,一次一个。
当我遍历 FOR LOOP
来获取用户时,不知何故获得的顺序不正确,它混合了用户顺序。
我希望看到 user1
、user2
、user1
、user2
等...
但是我得到的user1
、user1
、user2
、user1
都是混合的并且顺序很奇怪,如果我刷新给定的顺序就会不同来自上一个。
即使我在 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
我有一个使用 CloudKit
作为后端的应用程序。
我有一个 Post
记录类型,其 user
为 CKReference
,以便跟踪谁是 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
创建的,post3
由 user1
创建,post4
由 user2
创建(依此类推...)
10 post 由这两个用户创建,一次一个。
当我遍历 FOR LOOP
来获取用户时,不知何故获得的顺序不正确,它混合了用户顺序。
我希望看到 user1
、user2
、user1
、user2
等...
但是我得到的user1
、user1
、user2
、user1
都是混合的并且顺序很奇怪,如果我刷新给定的顺序就会不同来自上一个。
即使我在 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