2

当我使用CKModifyRecordsOperation将多个表的记录保存到私有云数据库的默认区域时,它总是返回以下错误,但表“X”除外:

将记录保存到服务器时出错:将记录从“X”类型更新为“Y”的尝试无效

error.userInfo细节:

{
CKErrorDescription = "将记录 CKRecordID: 0x7fd7a3d4c0c0; 1:(_defaultZone: defaultOwner ) 保存到服务器时出错:将记录从'X'类型更新为'Y'的尝试无效";
ContainerID = "iCloud.com...";
NSDebugDescription = "CKInternalErrorDomain: 2006";
NSLocalizedDescription = "将记录保存到服务器时出错:将记录从'X'类型更新为'Y'的尝试无效";
NSUnderlyingError = "CKError 0x7fa0d250c4e0: \"Invalid Arguments\" (2006); 服务器消息 = \"从类型 'X' 到 'Y' 更新记录的尝试无效\"; uuid = E2E...D1E; 容器 ID = \ "iCloud.com...\"";
RequestUUID = "E2E...

错误键 = ck1rosofi;
}

相关代码片段:

- (void)sync
{
  ...
  NSMutableArray * operations;
  for (NSString *tableName in @[@"X", @"Y"]) {
    CKModifyRecordsOperation * operation = [self _modifyRecordsOperationWithTableName:tableName];
    if (operation) {
      if (operations) [operations addObject:operation];
      else operations = [NSMutableArray arrayWithObject:operation];
    }
  }

  if (operations) {
    [operationQueue addOperations:operations waitUntilFinished:NO];
  }
}

- (CKModifyRecordsOperation *)_modifyRecordsOperationWithTableName:(NSString *)tableName
{
  ...

  NSMutableArray * recordsToSave = [NSMutableArray array];
  for (KYModel <KYModel_iCloudProtocol> *instance in unsyncedInstances) {
    CKRecordID * objectID = [[CKRecordID alloc] initWithRecordName:@(instance.id).stringValue];
    CKRecord * cloudRecord = [[CKRecord alloc] initWithRecordType:tableName recordID:objectID];
    ... setup record detail
    [recordsToSave addObject:record];
  }

  CKModifyRecordsOperation * operation = [[CKModifyRecordsOperation alloc] initWithRecordsToSave:recordsToSave recordIDsToDelete:nil];
  operation.database = [[CKContainer defaultContainer] privateCloudDatabase];
  operation.savePolicy = CKRecordSaveAllKeys;
  operation.qualityOfService = NSQualityOfServiceUserInteractive;
  operation.atomic = NO;
  operation.perRecordProgressBlock = ...;
  operation.perRecordCompletionBlock = ^(CKRecord *record, NSError *error) {
    if (error) {
      // got error here
    }
    ...
  };
  operation.modifyRecordsCompletionBlock = ...;

  return operation;
}
4

1 回答 1

3

经过一段时间的搜索和调试,我意识到那些失败的记录已经在同一个区域中具有相同的“记录名称”(它是一个唯一的名称CKRecordID,它就像每个记录的唯一 ID)已经在同一个区域中,尽管它们属于不同的表.


解决方案一:

使 CKRecordID 的“记录名称”在所有表中唯一

例如,为每个记录名添加一个表名前缀:[table_name]_[id]。

我上面的案例的问题是“记录名称”对于同一区域(默认区域)中不同表的记录重复。

并且使用CKModifyRecordsOperationCKRecordSaveAllKeys保存策略,当保存表 Y 的记录 (id:1) 时,它在 Cloud 中找到表 X 的另一条记录 (id:1),并尝试对其进行修改,这会引发错误“更新记录的尝试无效最后从类型'X'到'Y'”


解决方案二:

为每个表创建自定义区域(注意:自定义区域仅适用于私有云数据库)。

例如,不使用默认区域,而是为表 X 使用自定义区域“X_Zone”,为表 Y 使用“Y_Zone”。然后我们可以继续使用@“id”作为 CKRecordID 的“记录名称”。

这样,zoneID在将本地记录转换为 CKRecord 实例时,您还需要提供:

CKRecordZoneID *zoneID = [[CKRecordZoneID alloc] initWithZoneName:@"Custom_Zone_Name_Here"
                                                         ownerName:CKOwnerDefaultName];
CKRecordID *objectID = [[CKRecordID alloc] initWithRecordName:@(instance.id).stringValue zoneID:zoneID];
...

当然,您需要先创建相关的自定义区域:

CKRecordZone *zone = [[CKRecordZone alloc] initWithZoneName:@"Custom_Zone_Name"];
[[[CKContainer defaultContainer] privateCloudDatabase] saveRecordZone:zone completionHandler:...];

或同时创建多个:

NSMutableArray *zones = [NSMutableArray array];
for (NSString *zoneName in @[...]) {
  CKRecordZone *zone = [[CKRecordZone alloc] initWithZoneName:zoneName];
  [zones addObject:zone];
}
CKModifyRecordZonesOperation * operation = [[CKModifyRecordZonesOperation alloc] initWithRecordZonesToSave:zones recordZoneIDsToDelete:nil];
...
[[[CKContainer defaultContainer] privateCloudDatabase] addOperation:operation];

我已经测试了两者并按预期工作。


建议:

如果您的记录存储在私有云数据库中,我认为解决方案二将是一个不错的选择,正如 DOC 所说:

... 使用自定义区域来安排和封装私有数据库中的相关记录组。自定义区域也支持其他功能,例如将多条记录写入单个原子事务的能力。

将每个自定义区域视为与数据库中的每个其他区域分开的单个数据单元。在区域内,您可以像在其他任何地方一样添加记录。...

处理每个表的记录会更方便,例如删除表的区域而不是查询和删除同一区域中的表的记录。

但是请注意,如果您CKReference在表格之间使用,那么请不要为这些表格使用自定义区域,导致

... CKReference 类不支持跨区域链接,因此每个引用对象必须指向与当前记录在同一区域中的记录。


顺便说一句,这是自我回答问题的答案,希望如果其他人遇到同样的问题,它会有所帮助。如果我的回答有问题,请指出,也欢迎任何建议,谢谢。:)

于 2016-05-17T13:44:22.267 回答