gpt4 book ai didi

ios - 如果在使用 CKFetchRecordZoneChangesOperation 时应用程序被杀死会怎样?

转载 作者:搜寻专家 更新时间:2023-11-01 07:02:26 24 4
gpt4 key购买 nike

假设您正在使用 CKFetchRecordZoneChangesOperation 获取任何更改。

您已成功获取所有已更改的CKRecords,但是,您目前尚未创建本地缓存。但是您确实在 recordZoneChangeTokensUpdatedBlock 中缓存了一个新的更改 token 您正准备为 CKRecords 创建一个本地缓存,但不知何故您的用户决定终止您的应用程序,并且您被终止。

重新启动后,你想重新下载更改,但现在有了新的更改 token ,从新 token 开始,没有任何更改。

如何解决?


有人指出我可以在记录写入本地数据库后缓存 token 。

但这并不总是可能的,因为在应用程序的初始启动时,应用程序可能必须下载大量 CKRecords 如果您不及时处理它们会消耗内存,在另一方面,CKFetchRecordZoneChangesOperation 使用两个不同的 block ,一个用于从服务器获取的新记录,另一个用于 token 更新。因此,您将不得不编写复杂的代码来协调这两个 block 。

var recordChangedBlock
var recordZoneChangeTokensUpdatedBlock

最佳答案

在您更新本地记录缓存之后之前,不要缓存您的更改 token 。对我有用...

要添加的 EDII:

让 CloudKit 同步是一件复杂的事情——至少对我来说是这样!我是 iOS 的新手,我花了很长时间才让我的系统正常工作——通过学习操作和 GCD 走了弯路。您将需要将其拆分为不同的操作或方法才能成功完成。就像您说的那样,同步可以随时中断/取消,您的系统需要对此具有弹性。

我建议不要使用 CKFetchRecordZoneChangesOperation 来处理任何事情,只是用来获取那些结果和标记,然后将它们传递给系统中的下一步。我正在使用操作,并且我有一个 CKFetchRecordZoneChangesOperation 的包装器操作 - 它是获取、修改和上传更改的链中的一个步骤。这不是整个类(class),只是适用于该问题的相关片段,让您了解我的意思。您的代码将(可能非常)不同:

class FetchRecordZoneChangesOperation: AsyncOperation {

// MARK: - Properties

var inputRecordZoneIDs: [CKRecordZoneID]?

var outputCKRecords: [CKRecord]?
var outputDeletedRecordIDs: [CKRecordID]?
var outputServerChangeToken: CKServerChangeToken?

var useServerChangeToken = true

override func main() {

if self.isCancelled {
self.finish()
return
}

if let recordZoneIDsDependency = dependencies
.filter({ $0 is CKRecordZoneIDsProvider })
.first as? CKRecordZoneIDsProvider
, inputRecordZoneIDs == nil {
inputRecordZoneIDs = recordZoneIDsDependency.ckRecordZoneIDs
}

// This record zone stuff is kinda redundant but it works...
var recordZoneID: CKRecordZoneID
var recordZoneIDs: [CKRecordZoneID]

if let zoneIDs = self.inputRecordZoneIDs, let zoneID = zoneIDs.first {

recordZoneID = zoneID
recordZoneIDs = zoneIDs

} else {

recordZoneID = UserDefaults.standard.ckCurrentRecordZoneID
recordZoneIDs = [UserDefaults.standard.ckCurrentRecordZoneID]

}

let operation = CKFetchRecordZoneChangesOperation()
// QOS

operation.qualityOfService = .userInitiated

operation.recordZoneIDs = recordZoneIDs

// if I have a database change token, use that, otherwise I'm getting all records

if useServerChangeToken, let token = UserDefaults.standard.ckRecordZoneChangeToken {

// TODO: This will change when I have more than 1 list

let fetchOptions = CKFetchRecordZoneChangesOptions()
fetchOptions.previousServerChangeToken = token
operation.optionsByRecordZoneID = [ recordZoneID : fetchOptions]
}

operation.recordChangedBlock = { record in
if self.outputCKRecords != nil {
self.outputCKRecords?.append(record)
} else {
self.outputCKRecords = [record]
}
}

operation.recordWithIDWasDeletedBlock = { recordID, somethingStringy in
if self.outputDeletedRecordIDs != nil {
self.outputDeletedRecordIDs?.append(recordID)
} else {
self.outputDeletedRecordIDs = [recordID]
}
}

operation.recordZoneChangeTokensUpdatedBlock = { recordZoneID, serverChangeToken, clientChangeTokenData in
self.outputServerChangeToken = serverChangeToken
}

operation.recordZoneFetchCompletionBlock = { recordZoneID, serverChangeToken, clientChangeTokenData, moreComing, error in

if error != nil {


cloudKit.errorController.handle(error: error, operation: .fetchChanges)


} else {

// Do I need to handle things with the clientChangeTokenData? Working flawlessly without currently. Right now I just store the server one

self.outputServerChangeToken = serverChangeToken

}
}
}

然后下一个操作会在本地修改记录:

class ModifyObjectsOperation: AsyncOperation {

var debug = true
var debugMore = false

var inputCKRecords: [CKRecord]?
var inputDeleteIDs: [CKRecordID]?

var inputRecordZoneChangeToken: CKServerChangeToken?
var inputDatabaseChangeToken: CKServerChangeToken?

override func main() {

if isCancelled {
self.finish()

return
}

if let recordDependency = dependencies
.filter({ $0 is CKRecordsProvider })
.first as? CKRecordsProvider
, inputCKRecords == nil {
inputCKRecords = recordDependency.ckRecords
}

if let recordZoneTokenDependency = dependencies
.filter({ $0 is CKRecordZoneTokenProvider })
.first as? CKRecordZoneTokenProvider
, inputRecordZoneChangeToken == nil {
inputRecordZoneChangeToken = recordZoneTokenDependency.ckRecordZoneChangeToken
}

if let databaseTokenDependency = dependencies
.filter({ $0 is CKDatabaseTokenProvider })
.first as? CKDatabaseTokenProvider
, inputDatabaseChangeToken == nil {
inputDatabaseChangeToken = databaseTokenDependency.ckDatabaseChangeToken
}

if let deleteDependency = dependencies
.filter({ $0 is CKRecordIDsProvider })
.first as? CKRecordIDsProvider
, inputDeleteIDs == nil {
inputDeleteIDs = deleteDependency.ckRecordIDs
}

if self.inputCKRecords == nil && self.inputDeleteIDs == nil {
if self.debug {
print("📚 ModifyObjectsOperation - no changes or deletes 👍🏼")
}

self.finish()
return
}

// NOW MODIFY YOUR RECORDS HERE. IF SUCCESSFUL, CACHE YOUR TOKEN.

}

如果您有大量数据要同步或有大量记录,那么这将需要多次旅行是对的。上传比下载更痛苦...有 200 条记录的上传限制(虽然我发现这并没有严格执行 - 我一次成功上传了多达 400 条小记录)。我发现我可以在一个 block 中下载数千条小记录。

真正知道您是安全的唯一方法是等到您拥有更改、在本地缓存您的更改,然后才保存该 changeToken。您宁愿获得重复数据也不愿在此过程中丢失某些东西。如果你做对了,你永远不会失去一些东西,你最终可能会做一些多余的工作。

编辑 2:

刚刚看到你上面问题的最后一部分。如果您的应用程序依赖于远程数据才能使用,那么您似乎只有几个选择。提供某种他们在使用该应用程序之前必须等待的进度指示器。或者重新构建您所拥有的,以便您可以在拥有该远程数据之前使用该应用程序。它可能不是最终状态,但至少可以使用。

对于我正在处理的工作,初始同步时可能有数万或数十万条记录,在使用该应用程序之前等待所有记录都同步是行不通的。他们可以开始使用该应用程序,并且可以在他们使用和更改基础数据时进行同步,因为它的构建具有弹性。

关于ios - 如果在使用 CKFetchRecordZoneChangesOperation 时应用程序被杀死会怎样?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50354368/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com