在后台更新NSManagedObjects

时间:2014-07-25 00:03:10

标签: ios core-data afnetworking grand-central-dispatch magicalrecord

我正在尝试根据网络提取的属性在后台更新NSManagedObject。难以绕过并发。我试过的是

  1. 在localcontext上获取NSManagedObjects(Asset)(NSPrivateQueueType)
  2. 枚举资产数组为每个资产执行网络GET请求并将其添加到dispatch_group
  3. 在网络电话的完成版块内,将更新后的值映射到资源(这是我的问题)
  4. 我的代码看起来像这样

    __block dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        __block dispatch_group_t taskGroup = dispatch_group_create();
    
        [MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
            NSArray *assets = [Asset MR_findAllInContext:localContext];
            for (Asset *asset in assets) {
                dispatch_group_enter(taskGroup);
                dispatch_async(queue, ^{
                    [stockClient GET:@"/v1/public/yql" parameters:asset.assetSymbol success:^(NSURLSessionDataTask *task, id responseObject) {
                       //TODO:Check response
                        NSDictionary *stockData = [[[responseObject objectForKey:@"query"] objectForKey:@"results"] objectForKey:@"quote"];
                        [asset mapPropertiesFrom:stockData];//----------> How do I access the localcontext queue?
                        dispatch_group_leave(taskGroup);
    
                    } failure:^(NSURLSessionDataTask *task, NSError *error) {
                        //TODO:Error handling here
                        dispatch_group_leave(taskGroup);
                    }];
                });
            }
    
            dispatch_group_notify(taskGroup, dispatch_get_main_queue(), ^{
                NSLog(@"All Tasks are completed");
            });
        }];
    }
    

    当我尝试从网络完成块

    更新资产(NSManagedObject)时
    [asset mapPropertiesFrom:stockData];
    

    我收到了

      

    CoreData无法解决

    的错误

    我怀疑这是因为完成块在主队列上,我的资产是在私有队列上获取的,你不能从不同的队列访问NSManagedObjects ......但是我不确定这是问题所在如果是如何解决它。

    那么,我怎么能完成这样的任务呢?我完全错了吗?

    修改

    根据以下评论进行了一些更改,但我收到了错误

    //Setup Dispatch Group
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_group_t taskGroup = dispatch_group_create();
    
        NSArray *assets = [Asset MR_findAll];
        for (Asset *asset in assets) {
            dispatch_group_enter(taskGroup);
            dispatch_async(queue, ^{
                NSManagedObjectContext *localContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
                localContext.parentContext = [NSManagedObjectContext MR_defaultContext];
                Asset *localAsset = [asset MR_inContext:localContext];
                [stockClient updateStockDataFor:localAsset completionHandler:^(NSDictionary *stockData, NSError *error) {
                    NSManagedObjectContext *responseContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
                    responseContext.parentContext = [NSManagedObjectContext MR_defaultContext];
                    Asset *responseAsset = [localAsset MR_inContext:responseContext];
                    [responseAsset mapPropertiesFrom:stockData];
                    [responseContext MR_saveToPersistentStoreAndWait];
                     dispatch_group_leave(taskGroup);
    
                }];
            });
        }
        dispatch_group_notify(taskGroup, dispatch_get_main_queue(), ^{
            NSLog(@"Tasks are Completed");
        });
    }
    

    错误:

      

    只能在NSManagedObjectContext上使用-performBlock :.   用队列创建。

2 个答案:

答案 0 :(得分:2)

使用以下方法包装将在执行块中读取/写入核心数据的任何调用:

- (void)performBlock:(void (^)())block

来自文档:

  

如果是上下文,则使用此方法将消息发送到托管对象   使用NSPrivateQueueConcurrencyType或初始化   NSMainQueueConcurrencyType。

     

此方法封装自动释放池和调用   processPendingChanges。

只要您使用此方法进行通话,就不会有任何问题。

我不明白你问题的第二部分,但在我的应用程序中,我一直使用这种方法从网络调用执行更新/读取。或者,您只需将核心数据部分调度到主队列即可。也就是说,在后台执行网络操作以及何时需要核心数据时使用调度队列到主队列。

答案 1 :(得分:1)

就MagicalRecord而言,您将在后台调度后台任务。基本上,您的队列不同步。我建议您使用单个后台队列来保存数据。

尝试这样的事情:

__block dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
__block dispatch_group_t taskGroup = dispatch_group_create();


    NSArray *assets = [Asset MR_findAll];
    for (Asset *asset in assets) {
        dispatch_group_enter(taskGroup);
        dispatch_async(queue, ^{
            NSManagedObjectContext = //create a confinement context
            Asset *localAsset = [asset MR_inContext:localContext];
            [stockClient GET:@"/v1/public/yql" parameters:asset.assetSymbol success:^(NSURLSessionDataTask *task, id responseObject) {
               //TODO:Check response
                NSManagedObjectContext *responseContext = //create a confinement context
                Asset *responseAsset = [localAsset MR_inContext:responseContext];
                NSDictionary *stockData = [[[responseObject objectForKey:@"query"] objectForKey:@"results"] objectForKey:@"quote"];
                [asset mapPropertiesFrom:stockData];//----------> How do I access the localcontext queue?
                [responseContext MR_saveToPersistentStoreAndWait];
                dispatch_group_leave(taskGroup);

            } failure:^(NSURLSessionDataTask *task, NSError *error) {
                //TODO:Error handling here
                dispatch_group_leave(taskGroup);
            }];
        });
    }

    dispatch_group_notify(taskGroup, dispatch_get_main_queue(), ^{
        NSLog(@"All Tasks are completed");
    });

}