【问题标题】:CoreData: Can only use -performBlock: on an NSManagedObjectContext that was created with a queueCoreData:只能在使用队列创建的 NSManagedObjectContext 上使用 -performBlock:
【发布时间】:2014-08-27 09:17:03
【问题描述】:

我不确定这是怎么回事,但我可以寻求帮助。每次用户在 UITextField 中输入文本时,我都会尝试在后台执行查询。我一直在阅读,看起来这就是我应该如何执行后台 CoreData 操作,但我不断收到此错误:

“只能在使用队列创建的 NSManagedObjectContext 上使用 -performBlock:”

我用谷歌搜索了这个错误,但每个解决方案都说我的上下文需要使用 PrivateQueueConcurrentcyType 创建,我这样做了。不知道为什么会这样。可能是新 iOS 的 bug?

let managedObjectContext = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = appDelegate.persistentStoreCoordinator
if !textField.text.isEmpty {
    manatedObjectContext.performBlock {
         let objectIDs = //query to get the object IDs

         appDelegate.managedObjectContext.performBlock {
             var objects = Object[]()

             for id in objectsIDs {
                 objects += appDelegate.managedObjectContext.objectWithID(id) as Object
             }

             self.searchResults = objects
             self.searchResultsTableView.reloadData()
         }
    }
}

编辑:我通过执行以下操作解决了我的问题:

  • 将私有 managedObjectContext 的父上下文设置为来自 AppDelegate 的主上下文
  • 我删除了设置持久存储的行,因为在将父上下文设置为主上下文时不再需要它
  • 我还更改了创建主上下文的默认实现,以使用 MainQueueConcurrencyType 显式创建它

【问题讨论】:

  • Nice Swift (-: 你可能对managedObjectContext.psc = appDelegate.psc 有问题。你可能应该让你的threadMOC 依赖 使用mainMOC 来代替-setParentContext: . 请参阅下面的完整示例。
  • @gothicdev 谢谢!设置父上下文并更改使用“MainQueueConcurrencyType”显式创建的主上下文修复了该问题。

标签: ios objective-c core-data swift


【解决方案1】:

示例取自实际发布的应用程序,您应该使用 2+ 个 MOC。

  1. 一个用于主线程
  2. 每个后台线程一个。

这允许后台线程安全地执行 MOC 操作,而不会有相互冲突的风险。请注意辅助 MOC 如何使用 -setParentContext: 来引用主 MOC。

这个backgroundMOC,当从后台线程创建和使用时,是安全的,不会抛出错误。

完成修改后,在后台线程上执行保存:

[backgroundMOC performBlockAndWait:^{
    if([backgroundMOC hasChanges]) {
        NSError * error;
        [backgroundMOC save:&error];
        // handle error
    }
}];

要创建上面示例中使用的两个 MOC,您可以使用以下代码 (ObjC)

对于主线程,使用:

@property(strong) NSManagedObjectContext * 

- (NSManagedObjectContext *)mainMOC mainManagedObjectContext;
{
    NSThread * currentThread = [NSThread currentThread];
    NSAssert([currentThread isMainThread], @"managedObjectContext invoked from %@",currentThread);
    if([currentThread isMainThread]) {
        if(!_mainManagedObjectContext) {
            NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];

            if(coordinator) {
                _mainManagedObjectContext = [[NSManagedObjectContext alloc]
                                             initWithConcurrencyType:NSMainQueueConcurrencyType];
                [_mainManagedObjectContext setPersistentStoreCoordinator: coordinator];
                [_mainManagedObjectContext setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
            }
        }

        return _mainManagedObjectContext;
    }
    return nil;
}

对于后台线程,使用:

- (NSManagedObjectContext *)threadMOCWithMainMOC:(NSManagedObjectContext *)mainMOC
{
    NSManagedObjectContext * threadManagedObjectContext = [[NSManagedObjectContext alloc]
                                       initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    [threadManagedObjectContext setParentContext:[self.db mainManagedObjectContext]];
    [threadManagedObjectContext setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
    return threadManagedObjectContext;
}

【讨论】:

  • 要使用后台moc,需要在dispatch_async(dispatch_get_global_queue...中包裹相应的代码吗?如果您能提供一个如何正确使用背景上下文的示例,那将非常有帮助,谢谢!
  • 是的。后台线程可以是单个方法,例如用于下载文件的方法,如以下答案所述:stackoverflow.com/questions/7055424/ios-start-background-thread
【解决方案2】:

问题不在于您在提供的代码中构建的 MOC,而在于您从应用委托中获得的 MOC。我敢打赌,这是用苹果提供的核心数据模板构建的......(非常不幸)仍然使用默认限制类型创建 MOC......

这就是它所抱怨的……

appDelegate.managedObjectContext.performBlock {

【讨论】:

  • 不,它与第一次 performBlock 调用有关。对不起,应该在前面说。我通过将私有上下文的父级设置为主上下文来解决此问题。此外,我更改了创建主上下文的方式,明确表示它是“MainQueueConcurrencyType”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-02-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多