从异步Web服务响应更新管理对象的最佳方法? [英] Best approach to update managed objects from async web services responses?

查看:156
本文介绍了从异步Web服务响应更新管理对象的最佳方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 NSManagedObjectContext 关联到主线程( mainContext ),其中我获取所有 NSManagedObject 我在整个应用程序中显示。



用户不会编辑这些对象,我定期对这些服务执行异步调用,并且告诉我必须删除哪些受管对象必须被删除(如果有的话),哪些必须用新信息(如果有的话)更新,如果我需要插入新对象。



所以,我需要首先获得所有服务的响应,然后检查我要对我已经在我的 mainContext 。我还需要执行更新,避免阻止UI。



我正在考虑两种方法来管理这种情况:


  1. 要在私有队列中使用一个完全独立的 privateContext ,并使用自己的Core Data栈来插入我从服务中获得的所有对象。然后比较某些(如何?)与我在 mainContext 中的对象,并删除/修改/插入中的对象mainContext

  2. 在私有队列中使用 privateContext ,但是 mainContext 。然后我需要传递子上下文在其父 mainContext (这是可能的?如何?)的对象,同时插入这个子上下文


  3. 这些方法是最好的还是最合适的方法?



    提前感谢



    EDIT:这是另一种可能的方式吗?


    1. 只能使用 mainContext ,因为我正在解析服务的响应,而不是创建新对象只是在 mainContext 上逐一进行更改。 ..

    编辑2:另一种可能性?:


    1. 仅使用 privateContext ,获取服务响应并创建新对象。然后,还使用这个 privateContext 获取所有已经存在的对象(这将与 mainContext )。在 privateContext 中进行更改,比较两组对象(最近从服务和获取的对象创建),保存此上下文,清除 mainContext 并重新获取 mainContext 中的所有对象。


    解决方案

    我不知道这是一个完整的答案,但我的工作有点类似的情况。我所采取的实现路径是使用子对象(所有的地方) - 或更像临时子上下文根据需要。



    我应该提到的第一件事,但是,确保使用 CoreData 调试功能构建到XCOde中。我做了第二个运行方案,它有



    -com.apple.CoreData.ConcurrencyDebug 1





    在应用程序init上有我的正常 NSManagedObjectContext 周围到我的背景网络线程。

      NSManagedObjectContext * childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; 
    [child setParentContext:parentObjectContext];

    然后每当我需要从父母传给子女时,我最终会做:

      [child objectWithID:(object-in-parent-context)] 

    或我最后做了

      __ block AHRS_RPYL * ret; 

    [[self getChildContext] performBlockAndWait:^ {

    ret = [NSEntityDescription insertNewObjectForEntityForName:@RPYLinManagedObjectContext:[self getChildContext]];

    // ...很多代码
    }];

    我不能说我真的爱这种方法,然而它似乎工作得很好。



    在大多数视图控制器之间我有一个

      @synthesize managedObjectContext; 

    在我的 prepareForSegue 方法

      //传递managedObjectContext 
    - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {

    // dispatch_async(dispatch_get_main_queue(),^ {
    //如果目标VC能够使用setManagedObjectContext方法,当前的objectContext将被传递。 destinationViewController responsesToSelector:@selector(setManagedObjectContext :)]){
    [segue.destinationViewController performSelector:@selector(setManagedObjectContext :)
    withObject:self.managedObjectContext];

    } else {

    NSLog(@Segue to controller [%@] that does not support passing managedObjectContext,[segue destinationViewController]);
    }
    //});
    }



    摘要




    • 我已经使用你的第二选择选项,它是可行的,虽然它感觉有点笨重在我的诚实意见。事实是,我们正在寻找切换到Realm而不是 CoreData ,因为无论你怎么看它 CoreData 不是



    • 保存代码



      我实际上保存到子上下文每50条消息和父250每个它看起来像(年龄没有触及这个代码)。我不能保证这是最好的正确方式做事情,但它的工作,它确保磁盘访问的最小。我得到一大堆消息像20+一秒钟,所以我想做这种堆栈类型。您可能不在乎



      (self.getChild())返回子上下文 - 其旧代码我留在这里。

        if(msgCount%50 == 0){
      // Child Save!

      // NSLog(@Saving SDatas);
      __block NSManagedObjectContext * currentChild = [self getChildContext];
      [self incChildContext];

      //父子保存方法
      [currentChild performBlock:^ {
      NSError * error;
      if(![currentChild save:& error]){
      NSLog(@\\\
      error =>%@ \\\
      ,[error localizedDescription]);
      NSLog(@error =>%@,[error userInfo]);
      [NSException raise:@数据库写错误格式:@%@%@,[error localizedDescription],[error userInfo]];

      // abort();
      }

      if(msgCount%250< 5){

      [parentObjectContext performBlock:^ {
      NSError * error;
      if(![parentObjectContext save:& error]){
      NSLog(@\\\
      error =>%@ \\\
      ,[error localizedDescription]);
      NSLog(@error =>%@,[error userInfo]);
      [NSException raise:@数据库写错误格式:@%@%@,[error localizedDescription],[error userInfo]];

      // abort();


      }
      }];
      }

      [currentChild reset];
      }];


      }



      删除



        [childContext performBlock:^ {

      // LOTS OF CODE
      //构建一个查询集我们要杀死
      //结束大量代码
      [childContext deleteObject:msg];


      if(i%modFactor == 0){
      self.percentDone = i / totalRecords;

      NSLog(@%。1f Saving ...,self.percentDone * 100);


      NSError * error;
      if(![childContext save:& error]){
      NSLog(@\\\
      error =>%@ \\\
      ,[error localizedDescription]);
      NSLog(@error =>%@,[error userInfo]);
      [NSException raise:@数据库写错误格式:@%@%@,[error localizedDescription],[error userInfo]];

      // abort();
      }

      [parentContext performBlock:^ {
      NSError * errrror;
      if(![parentContext save:& errrror]){
      NSLog(@\\\
      error =>%@ \\\
      ,[error localizedDescription]);
      NSLog(@error =>%@,[error userInfo]);
      [NSException raise:@数据库写错误格式:@%@%@,[error localizedDescription],[error userInfo]];

      // abort();
      }
      }];
      }

      }];



      ...



      我说之前这可能不是做事情的最好的方式,但我有一个父/子堆栈,它的工作。这是我写的第一个iOS应用程序之一,并再次做,我会踢核心数据和使用别的东西。



      我们有一个非常高的更新率,所以当我们使用单个堆栈,它使UI非常慢。迁移到父/子是缓慢的 - 我是做它再次我认为它会更顺利,因为我会写很多帮助函数来处理这些(或只是使用swift)。



      祝你好运。


      I have an NSManagedObjectContext associated to main thread (mainContext), where I fetch all the NSManagedObject I show throughout the app.

      Users don't edit these objects, but I get updates from web services. I periodically perform asynchronous calls to such services, and they "tell" me which of my managed objects have to be deleted (if any), which of them have to be updated with new information (if any), and if I need to insert new objects.

      So, I need to firstly get the responses of all the services and then check what changes I have to make to the managed objects I already have in my mainContext. And I also need to perform the updates avoiding blocking the UI.

      I was thinking about 2 ways to manage this scenario:

      1. To use a completely separated privateContext in a private queue with its own Core Data stack to insert there all the objects I get from services. Then compare somehow (how?) with the objects I have in mainContext, and delete/modify/insert objects that are there in mainContext.
      2. To use a privateContext in a private queue, but being a child of the mainContext. I'd then need to pass the child context the objects I have in its parent mainContext (is this possible? how?), and at the same time insert in this child context the objects I get from services, and then compare and perform changes.

      What of the approaches would be the best or the appropriate one? Or maybe should it be a different one I haven't thought about?

      Thanks in advance

      EDIT: Could this be another possible way?:

      1. Only use the mainContext and, as I'm parsing the responses of the services, instead of creating the new objects just make changes on mainContext one by one...

      EDIT 2: Another possibility?:

      1. Only use the privateContext, get the services responses and create the new objects. Then, also fetch with this privateContext all the objects that already existed (and that would be the same as the objects in mainContext). Make changes in this privateContext comparing the two sets of objects (the recently created from services and the fetched), save this context, clear mainContext and re-fetch all objects in mainContext.

      解决方案

      I'm not sure this is a full answer for you but I'm working on a somewhat similar situation. The implementation path I've taken is to use child objects (all over the place) - or more like temporary child context's as needed.

      The first thing I should mention, however, is make sure to use the CoreData debug functionality build into XCOde. I made a second Run-Scheme that has

      -com.apple.CoreData.ConcurrencyDebug 1

      On application init I have my normal NSManagedObjectContext - which is passed around to my background networking thread.

      NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
         [child setParentContext:parentObjectContext];
      

      Then whenever I need to pass something from parent to child I end up doing:

      [child objectWithID:(object-in-parent-context)]
      

      or I end up doing

      __block AHRS_RPYL * ret;
      
      [[self getChildContext] performBlockAndWait:^{
      
          ret = [NSEntityDescription insertNewObjectForEntityForName:@"RPYL" inManagedObjectContext:[self getChildContext]];
      
      // ... lots of code
      }];
      

      I can't say I really "love" this approach and I'm sort of stuck with it for the moment however it does seem to work well enough.

      Between most of my view controllers I have a

      @synthesize managedObjectContext;
      

      And in my prepareForSegue method

      // Pass on managedObjectContext
      - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
      
          //    dispatch_async(dispatch_get_main_queue(), ^{
          // If the destination VC is able to take teh setManagedObjectContext method the current objectContext will be passed along.
          if ([segue.destinationViewController respondsToSelector:@selector(setManagedObjectContext:)]) {
              [segue.destinationViewController performSelector:@selector(setManagedObjectContext:)
                                                    withObject:self.managedObjectContext];
      
          } else {
      
              NSLog(@"Segue to controller [%@] that does not support passing managedObjectContext", [segue destinationViewController]);
          }
          //    });
      }
      

      Summary

      • I've worked with your second choice option and it is doable although it feels kind of clunky in my honest opinion. The truth is we are looking into switching to Realm instead of CoreData because no matter how you look at it CoreData is not the most threading friendly option.

      UDPATES

      Save Code

      I actually save to the child context every 50 messages and to the parent every 250 it looks like (haven't touched this code in ages). I can't promise this is the best correct way to do things but it does work and it does keep disc access to a minimum. I am getting A LOT of messages like 20+ a second so i wanted to do this stack type. You may not care

      (self.getChild()) returns the child context - its older code i left here.

      if (msgCount % 50 == 0) {
                      // Child Save!
      
                      //                    NSLog(@"Saving SDatas");
                      __block NSManagedObjectContext *currentChild = [self getChildContext];
                      [self incChildContext];
      
                      // Parent-Child save methodology
                      [currentChild performBlock:^{
                          NSError *error;
                          if (![currentChild save:&error]) {
                              NSLog(@"\n error => %@ \n", [error localizedDescription]);
                              NSLog(@" error => %@ ", [error userInfo]);
                              [NSException raise:@"Database Write Error" format:@"%@ %@", [error localizedDescription], [error userInfo]];
      
                              //                           abort();
                          }
      
                          if (msgCount % 250 < 5) {
      
                              [parentObjectContext performBlock:^{
                                  NSError *error;
                                  if (![parentObjectContext save:&error]) {
                                      NSLog(@"\n error => %@ \n", [error localizedDescription]);
                                      NSLog(@" error => %@ ", [error userInfo]);
                                      [NSException raise:@"Database Write Error" format:@"%@ %@", [error localizedDescription], [error userInfo]];
      
                                      //                                   abort();
      
      
                                  }
                              }];
                          }
      
                          [currentChild reset];
                      }];
      
      
                  }
      

      Deleting

       [childContext performBlock:^{
      
       // LOTS OF CODE
       // to build a query set of the records we want to kill
      // End lots of code
      [childContext deleteObject:msg];
      
      
              if (i % modFactor == 0) {
                  self.percentDone = i / totalRecords;
      
                  NSLog(@"%.1f Saving ...", self.percentDone * 100);
      
      
                  NSError *error;
                  if (![childContext save:&error]) {
                      NSLog(@"\n error => %@ \n", [error localizedDescription]);
                      NSLog(@" error => %@ ", [error userInfo]);
                      [NSException raise:@"Database Write Error" format:@"%@ %@", [error localizedDescription], [error userInfo]];
      
                      //                    abort();
                  }
      
                  [parentContext performBlock:^{
                      NSError *errrror;
                      if (![parentContext save:&errrror]) {
                          NSLog(@"\n error => %@ \n", [error localizedDescription]);
                          NSLog(@" error => %@ ", [error userInfo]);
                          [NSException raise:@"Database Write Error" format:@"%@ %@", [error localizedDescription], [error userInfo]];
      
                          //                        abort();
                      }
                  }];
              }
      
      }];  
      

      ...

      Again - like i said before this may not be the best way of doing things but i do have a parent/child stack and it does work. This was one of the first iOS apps I wrote and to do it over again i'd punt on core data and use something else.

      We had an extremely high update rate so when we were using a single stack it was making the UI very slow. The migration to parent/child was slow - were i to do it again I think it would have gone smoother because i'd write many helper functions to deal with some of this (or just use swift).

      good luck.

      这篇关于从异步Web服务响应更新管理对象的最佳方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆