CKFetchRecordsOperation + CKQueryOperations …我错过了什么?

在经过大量search示例代码之后,pipe理凑齐CKFetchRecordsOperation; 在这里,但是我一定错过了一些东西。 不要误解我的看法,但是…

要执行一个CKFetchRecordsOperation,你需要一个CKRecordID的NSArray; 为了得到一个CKRecordID的NSArray,你需要执行CKQuery,通过它你可以build立CKRecordID的NSArray。

但等一下,解压缩CKRecordIDs的过程使用一个CKQuery,通过它我可以简单地下载CKRecords呢?

如果没有CKQuery,你怎么得到CKRecordID的NSArray?

-(void)doSingleBeaconsDownload { CKDatabase *publicDatabase = [[CKContainer containerWithIdentifier:@"iCloud.cqd.ch.BeaconBrowser"] publicCloudDatabase]; NSPredicate *predicatex = [NSPredicate predicateWithFormat:@"iBeaconConfig = %@",iBeaconsConfirmed.giReferenceID]; CKQuery *query = [[CKQuery alloc] initWithRecordType:@"Running" predicate:predicatex]; [self.delegate performSelectorOnMainThread:@selector(processCompleted:) withObject:@"Downloading Configuration ..." waitUntilDone:YES]; [publicDatabase performQuery:query inZoneWithID:nil completionHandler:^(NSArray *results, NSError *error) { if (error) { NSLog(@"Batch Download Error iCloud error %@",error); } else { NSMutableArray *rex2download = [[NSMutableArray alloc] init]; for (CKRecord *rex in results) { [rex2download addObject:rex.recordID]; } CKFetchRecordsOperation *fetchRecordsOperation = [[CKFetchRecordsOperation alloc] initWithRecordIDs:rex2download]; /* fetchRecordsOperation.perRecordCompletionBlock = ^(CKRecord *record, CKRecordID *recordID, NSError *error) { if (error) { // Retain the record IDs for failed fetches } else { // Do something with each record downloaded } };*/ fetchRecordsOperation.perRecordProgressBlock = ^(CKRecordID *record, double recordsDownloaded) { if (error) { // damn ... } else { NSLog(@"Downloaded %f", recordsDownloaded); } }; fetchRecordsOperation.fetchRecordsCompletionBlock = ^(NSDictionary *recordsByRecordID, NSError *error) { if (error) { // Failed to fetch all or some of the records } else { for(CKRecord *record in results) { NSLog(@"Fini download %lu",(unsigned long)[recordsByRecordID count]); } [self.delegate performSelectorOnMainThread:@selector(beaconsDownloaded:) withObject:noOf waitUntilDone:YES]; } }; [publicDatabase addOperation:fetchRecordsOperation]; } }]; } 

来自Apple 文档 :CKFetchRecordsOperation对象从iCloud中检索CKRecord对象(其ID已经知道)。

CKQueryOperation是用来从iCloud检索基于某个Query的CKRecords,所以即使你不知道他们的recordID,你也可以得到它们。 一个CKFetchRecordsOperation只有当你有CKRecordID使用。 您可以在不访问iCloud的情况下创buildCKRecordID,并且可以将它们存储在任何本地存储中。

我使用这种操作的一个很好的用例是当你想修改一个CKRecord时,你需要先从iCloud(使用CKFetchRecordsOperation)中检索它,然后使用CKModifyRecordsOperation保存它。

看一看CloudKit上的两个WWDC 2014video就能很好地解释这一点。

谢谢你的帮助! 我成功地在代码中创build了一个CKQueryOperation,但是…但是我的代码很快就会被更多的嵌套循环变得不可读? 当然有一个更优雅的方式来链接CKQuery /取/修改操作; 尝试依赖,但仍然失去了一些东西?

 -(void)doSingleBeaconsDownload { [self.delegate performSelectorOnMainThread:@selector(processCompleted:) withObject:@"Downloading Configuration ..." waitUntilDone:YES]; CKDatabase *publicDatabase = [[CKContainer containerWithIdentifier:@"iCloud.cqd.ch.BeaconBrowser"] publicCloudDatabase]; NSPredicate *predicatex = [NSPredicate predicateWithFormat:@"iBeaconConfig = %@",iBeaconsConfirmed.giReferenceID]; CKQuery *query = [[CKQuery alloc] initWithRecordType:@"Running" predicate:predicatex]; CKQueryOperation *queryOp =[[CKQueryOperation alloc] initWithQuery:query]; queryOp.desiredKeys = @[@"record.recordID.recordName"]; queryOp.resultsLimit = CKQueryOperationMaximumResults; NSMutableArray *rex2download = [[NSMutableArray alloc] init]; queryOp.recordFetchedBlock = ^(CKRecord *results) { [rex2download addObject:results.recordID]; }; queryOp.queryCompletionBlock = ^(CKQueryCursor *cursor, NSError *error) { // Cursor it seems contains a reference to a second call to it [required] if you try download more then 100 records if (error) { NSLog(@"Batch Download Error iCloud error %@",error); } else { CKFetchRecordsOperation *fetchRecordsOperation = [[CKFetchRecordsOperation alloc] initWithRecordIDs:rex2download]; fetchRecordsOperation.perRecordCompletionBlock = ^(CKRecord *record, CKRecordID *recordID, NSError *error) { if (error) { // Retain the record IDs for failed fetches } else { // Do something .. } }; fetchRecordsOperation.perRecordProgressBlock = ^(CKRecordID *record, double recordsDownloaded) { if (error) { // damn } else { NSLog(@"Downloaded X"); } }; fetchRecordsOperation.fetchRecordsCompletionBlock = ^(NSDictionary *recordsByRecordID, NSError *error) { if (error) { // Failed to fetch all or some of the records } else { … Final clean up } }; [publicDatabase addOperation:fetchRecordsOperation]; } }; [publicDatabase addOperation:queryOp]; } 

感谢Harry; 这是我的第三个也是最后的工作scheme。 使用一个单例全局variables来传递两个CKQueryOperations之间的数据; 不知道这是最好的/好的做法,但它的工作原理

…可惜你不能用这样的东西…

 [fetchRecordsOperation addDependency:queryOp]; & [queue fetchRecordsOperation]; (doesn't compile) 

将是一个更清洁的解决scheme…无论如何这里是完整的V3 ..

 -(void)doSingleBeaconsDownloadV3 { NSLog(@"doQuery executing"); CKDatabase *publicDatabase = [[CKContainer containerWithIdentifier:@"iCloud.cqd.ch.BeaconBrowser"] publicCloudDatabase]; NSPredicate *predicatex = [NSPredicate predicateWithFormat:@"iBeaconConfig = %@",iBeaconsConfirmed.giReferenceID]; CKQuery *query = [[CKQuery alloc] initWithRecordType:@"Running" predicate:predicatex]; CKQueryOperation *queryOp =[[CKQueryOperation alloc] initWithQuery:query]; queryOp.desiredKeys = @[@"record.recordID.recordName"]; queryOp.resultsLimit = CKQueryOperationMaximumResults; //NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:NO]; //query.sortDescriptors = @[sortDescriptor]; queryOp.recordFetchedBlock = ^(CKRecord *results) { [iBeaconsConfirmed.giRex2Download addObject:results.recordID]; NSLog(@"fetched %lu",[iBeaconsConfirmed.giRex2Download count]); }; queryOp.queryCompletionBlock = ^(CKQueryCursor *cursor, NSError *error) { if (error) { NSLog(@"Batch Download Error iCloud error %@",error); } else { NSLog(@"fetched %lu",[iBeaconsConfirmed.giRex2Download count]); [self DoFetchV2]; } }; [publicDatabase addOperation:queryOp]; } -(void)DoFetchV2 { NSLog(@"dofetch executing %lu",[iBeaconsConfirmed.giRex2Download count]); CKDatabase *publicDatabase = [[CKContainer containerWithIdentifier:@"iCloud.cqd.ch.BeaconBrowser"] publicCloudDatabase]; CKFetchRecordsOperation *fetchRecordsOperation = [[CKFetchRecordsOperation alloc] initWithRecordIDs:iBeaconsConfirmed.giRex2Download]; fetchRecordsOperation.perRecordCompletionBlock = ^(CKRecord *record, CKRecordID *recordID, NSError *error) { if (error) { // Retain the record IDs for failed fetches } else { // Do something useful with data } }; fetchRecordsOperation.perRecordProgressBlock = ^(CKRecordID *record, double recordsDownloaded) { NSLog(@"Downloaded X"); }; fetchRecordsOperation.fetchRecordsCompletionBlock = ^(NSDictionary *recordsByRecordID, NSError *error) { if (error) { // Failed to fetch all or some of the records } else { NSLog(@"Fini download %lu",(unsigned long)[recordsByRecordID count]); } }; [publicDatabase addOperation:fetchRecordsOperation]; 

}