CKFetchRecordsOperation + CKQueryOperations ...我错过了什么?

CKFetchRecordsOperation + CKQueryOperations ... what am I missing?

在大量搜索示例代码后设法拼凑出一个 CKFetchRecordsOperation;在这里……但我一定错过了什么。别误会我的意思,它很有用……但是……

要执行 CKFetchRecordsOperation,您需要一个 CKRecordIDs 的 NSArray;要获得 CKRecordID 的 NSArray,您需要执行 CKQuery,通过它您可以构建 NSArray 的 CKRecordID。

但是等一下,提取 CKRecordID 的过程使用了 CKQuery,我可以通过它简单地下载 CKRecords 吗?

如果不使用 CKQuery,如何获取 NSArray of CKRecordID?

-(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 Documentation:CKFetchRecordsOperation 对象从 iCloud 检索 CKRecord 对象(您已经知道其 ID)。

CKQueryOperation 用于根据一些Query 从iCloud 中检索CKRecords,因此即使您不知道它们的recordID 也可以获取它们。仅当您拥有 CKRecordID 时才使用 CKFetchRecordsOperation。您可以在不访问 iCloud 的情况下创建 CKRecordID,并且可以将它们存储在您拥有的任何本地存储中。

我用于此类操作的一个很好的用例是,当您想要修改 CKRecord 时,您需要首先从 iCloud 检索它(使用 CKFetchRecordsOperation),然后使用 CKModifyRecordsOperation 将其保存回来。

看看 CloudKit 上的两个 WWDC 2014 视频,它们很好地解释了这一点。

感谢您的帮助!我设法在代码中制作了一个 CKQueryOperation,但是......但是我的代码很快就会因为更多的嵌套循环而变得不可读? link CKQuery/Fetch/Modify 操作肯定有更优雅的方式;尝试依赖但仍然遗漏了什么?

-(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];

}

谢谢哈利;这是我的第三个也是最后一个工作解决方案;使用单例全局变量在两个 CKQueryOperations 之间传递数据;不知道这是否是 best/good 实践,但它有效

...可惜你不能使用这样的东西...

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

将是一个更简洁的解决方案...无论如何这里是 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];

}