【问题标题】:Core data background fetch not working [closed]核心数据后台提取不起作用[关闭]
【发布时间】:2013-09-11 12:13:24
【问题描述】:
- (IBAction)findContact:(id)sender {

    CoreDataAppDelegate *appDelegate =
    [[UIApplication sharedApplication] delegate];

//    NSManagedObjectContext *context =
//        [appDelegate managedObjectContext];

    NSManagedObjectContext *managedContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    managedContext = [appDelegate managedObjectContext];

    //[context setParentContext:[self managedObjectcontext]];

    NSEntityDescription *entityDesc =
    [NSEntityDescription entityForName:@"Contacts"
                inManagedObjectContext:managedContext];

    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setEntity:entityDesc];

    NSPredicate *pred =
    [NSPredicate predicateWithFormat:@"(name == %@)",
     _name.text];
    [request setPredicate:pred];

    [managedContext performBlock:^{
        NSError *error = nil;
        NSArray *fetchedObjects = [managedContext executeFetchRequest:request error:&error];
        _objects = fetchedObjects;

            NSManagedObject *matches = nil;

            NSLog(@"data= %@", [_objects description]);
            if (_objects == nil) {
                _status.text = @"No matches";
            } else {
                matches = _objects[0];
                _address.text = [_objects valueForKey:@"address"];
                _phone.text = [_objects valueForKey:@"phone"];
                _status.text = [NSString stringWithFormat: @"%d matches found", [_objects count]];
            }

    }];
}

【问题讨论】:

    标签: iphone core-data background fetch


    【解决方案1】:

    首先,你能解释一下这样做的目的是什么:

    首先您创建一个具有私有并发类型的新上下文。

    NSManagedObjectContext *context = [[NSManagedObjectContext alloc]initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    

    现在你把那个上下文重新分配给别的东西了吗?

    context = [appDelegate managedObjectContext];
    

    现在您使用的上下文是您在 appDelegate 中定义的上下文...

    此外,假设您的 appDelegate 中定义的上下文是正确的,您将在上下文中异步执行获取请求。然后在以下几行中,您检查该提取的结果,而不知道提取请求是否已返回(因为它是异步的)。这解释了为什么您在获取结果后第二次看到结果。一个快速的解决方法是将一些 GCD 加入到上下文块中,如下所示:

    [context performBlock:^{        
        NSError *error = nil;
        NSArray *fetchedObjects = [context executeFetchRequest:request error:&error];
        dispatch_async(dispatch_get_main_queue(),^{
           _objects = fetchedObjects;
           if (_objects == nil) {
               _status.text = @"No matches";
           } else {
               NSLog(@"data= %@", [_objects description]);
               NSManagedObject *matches = _objects[0];
               _address.text = [matches valueForKey:@"address"];
               _phone.text = [matches valueForKey:@"phone"];
               _status.text = [NSString stringWithFormat: @"%d matches found", [_objects count]];
           }
        });
    }];
    

    我希望这会有所帮助?

    【讨论】:

    • 这不起作用,它显示了我分配匹配项 = _objects[0] 的错误,错误是“变量不可分配(缺少 _block 类型说明符)。
    • 抱歉,我编辑了答案并创建了一个临时变量来存储提取结果,然后将其分配给主队列中的实例变量。再试一次,它适用于我刚刚编写的示例应用程序......
    • 问题还是一样。我正在后台获取数据并将其显示到相应的文本字段。在这里,如果我在获取的对象中找到多个对象并将其分配给 NSManagedObject 匹配项,我将在搜索操作后分配第一个对象,但我无法做到这一点。在此先感谢...
    • 将匹配的声明放在块内而不是在块外声明,现在将编辑我的答案
    • 当您收到错误“变量不可分配(缺少 _block 类型说明符)”时,这意味着您正在尝试在该块之外创建的块内分配一个变量。由于块保留了块中使用的外部定义变量的副本,因此您必须在变量声明之前使用 __block 说明符,让编译器知道您将要在块中编辑变量,因此您不要不想要标准的只读副本。我建议查看 2012 WWDC session 712 关于异步设计模式:)
    【解决方案2】:

    消除执行块调用。获取是微不足道的,应该只需要一秒钟,所以可以在 UI 线程上执行它。

    不要让这个无关紧要的问题引诱您采用第三方框架,这些框架承诺简化,但自身也包含(无法控制的)复杂性。

    【讨论】:

      【解决方案3】:

      注意:以下代码未经测试

      要执行后台提取,您首先需要一个后台上下文。
      正如@Daniel_G 所说,您正在分配一个新上下文,然后用现有上下文(可能是您的主要上下文)覆盖它。

      要创建背景上下文,您需要NSPersistentStoreCoordinator:

      bgContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
      bgContext.persistentStoreCoordinator = //self.managedObjectContext.persistentStoreCoordinator (the main managed object context coordinator)
      //You can set here the undo-manager to nil as well
      

      现在我们有了一个实时后台上下文,我们可以执行提取并将数据导入到我们的导出上下文/线程:

      您的第一个选择是使用持久存储缓存并导入整个对象:
      (在这种情况下,您的 fetch 请求的结果类型是 NSManagedObjectResultType

      NSManagedObjectContext* exportContext = self.managedObjectContext; //capture the export context
      [bgContext performBlock:^{//Executed on a background thread (most likely not main)
          NSError* error = nil;
          NSArray* matched = [bgContext executeFetchRequest:request error:&error];
          //Check for errors, and count ...
          //
          //@note: objects contained in matched are not safe to access (even read only ops) in 
          //any other queue than this
          NSManagedObjectID* theId = [matched[0] objectID];
          [exportContext performBlockAndWait:^{ //keep the import context alive just in case
              //This code is executed on the calling object context queue (probably main)
              NSManagedObject* imported = [exportContext objectWithID:theID]; //Will not perform I/O
              //Extract the information you need from the imported data.
              //if your export context is not main, you will need to update UI elements
              //on the main thread (use GCD with main queue ...) and load the data to a
              //structure other than a NSManagedObject before moving to the main thread.
              //
              //@note: If the object matched is deleted after you've taken its ID and before you
              //       access it on this thread, an exception will be thrown
          }];
      }];
      

      这里的第二个选项(因为您似乎直接更新了 UI):
      像这样构建您的提取请求:

      NSFetchRequest* request = [NSFetchRequest fetchRequestWithEntityName:@"Contacts"];
      request.resultType = NSDictionaryResultType;
      request.propertiesToFetch = @[@"address",@"phone"];
      //Any other settings that you need for your request ...
      

      通过在后台上下文中执行此请求,您将获得一个包含您请求的信息的字典数组。
      这使得获取更有效(更快)。 您的提取代码应类似于:

      [bgContext performBlock:^{//Executed on a background thread (most likely not main)
          NSError* error = nil;
          NSArray* matched = [bgContext executeFetchRequest:request error:&error];
          //Check for errors, and count ...
          //
          //@note: matched is an array of dictionaries that can be passed between threads safely
          NSDictionary match = matched[0];
          dispatch_async(dispatch_get_main_queue(),^{//Executed on main thread
             _address.text = [match valueForKey:@"address"];
             _phone.text = [match valueForKey:@"phone"];
             _status.text = [NSString stringWithFormat: @"%d matches found", [matched count]];
          });
      }];
      

      一般来说,不要访问其上下文执行队列之外的托管对象(即使是读取操作也不行)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-08-28
        • 2010-12-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-19
        相关资源
        最近更新 更多