核心数据保存但不保存 - 专用队列跳转线程

Posted

技术标签:

【中文标题】核心数据保存但不保存 - 专用队列跳转线程【英文标题】:Core data saving but not saving - Dedicated queue jumping threads 【发布时间】:2015-10-07 22:42:34 【问题描述】:

我正在尝试在后台运行同步引擎,并且正在使用 NSPrivateQueueConcurrencyType,如下所示:

    - (NSManagedObjectContext *)workerContext


    NSManagedObjectContext* workerContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    workerContext.parentContext = self.mainContext;
    return workerContext;

我想我需要一个专用的后台队列,所以我也创建了这个:

- (dispatch_queue_t)syncQueue 
    if (!_syncQueue) 
        _syncQueue = dispatch_queue_create("com.me.syncEngineSyncQueue", 0);
    
    return _syncQueue;

我遇到了一个 NSManagedObject 删除正在发生的问题,但是,它被逆转了。我目前的猜测是它与线程有关。

我在同步引擎中记录了断点,并注意到相同的syncQueue 在不同的时间在不同的线程上运行。这可能是我的问题吗?

如果是这样,我如何创建一个在一个线程上一致运行的 dispatch_queue?

【问题讨论】:

【参考方案1】:

我想我需要一个专用的后台队列,所以我创建了这个 还有:

Bzzzzt。错误的。 Core Data 会为您解决这个问题。当您使用NSPrivateQueueConcurrencyType 初始化您的 MOC 时,它带有自己的内部管理的 GCD 队列。你根本不用管它。

但是,与该 MOC 相关的任何工作必须通过 performBlock API for NSManagedObjectContext 完成。除其他外,该 API 基本上将您的块放在特殊的私有 GCD 队列中,并安排它执行。

如果是这样,我如何创建一个在一个上始终运行的 dispatch_queue 线程?

你没有。这就是 GCD 之美的一部分。您将代码块插入消息队列,“一些”线程将它们剥离并执行它们。你可能不太关心哪个线程正在这样做(除了主队列的东西)。

编辑

明白了。但是,我仍然需要保存 mainContext 这是 我的workerContext的父母,对吧?我需要在 主线程?现在我正在做 [self.mainContext save:&error];在 使用保存workerContext后的主线程 performBlockAndWait – Ramsel 5 小时前

您最好将每个上下文视为自己的实体,并且仅“利用”在与 UI 元素交互时知道主上下文在主线程上运行,这需要在主线程上进行交互。

如果你想在保存子后保存父上下文,请执行以下操作,这是NSManagedObjectContext上的一个可能的类别方法。

- (void)_saveRecursively:(BOOL)recursiveSave
          withCompletion:(void(^)(NSError *error))completion 
    NSError *error;
    if ([self save:&error]) 
        error = nil;
        NSManagedObjectContext *parent = self.parentContext;
        if (parent != nil && recursiveSave) 
            return [parent performBlock:^
                [parent _saveRecursively:recursiveSave withCompletion:completion];
            ];
        
    
    completion(error);

上述方法是“私有的”,不应暴露。它保存当前上下文。如果要求进行递归保存,它将继续保存层次结构,直到它到达末尾,或者直到其中一个保存操作失败。

然后它将调用完成处理程序,如果失败则传递 NSError 的实例,如果成功则传递 nil

- (void)saveRecursively:(BOOL)recursiveSave
         withCompletion:(void(^)(NSManagedObjectContext *moc, NSError *error))completion 
    [self _saveRecursively:recursiveSave withCompletion:^(NSError *error) 
        if (completion) 
            [self performBlock:^
                completion(self, error);
            ];
        
    ];

此方法为用户提供类别 API。它确保从原始保存上下文的受保护范围内异步调用完成块。这样,您可以保证完成块可以安全使用,而无需调用另一个performBlock

- (void)saveWithCompletion:(void(^)(NSManagedObjectContext *moc, NSError *error))completion 
    [self saveRecursively:YES withCompletion:completion];

这为异步保存整个层次结构提供了一种方便的方法。

这意味着你可以像这样调用save方法...

[childContext saveWithCompletion:^(NSManagedObjectContext *moc, NSError *error) 
    // moc will be the same as childContext, and you can use the variable moc
    // safely, knowing this block is running in its own performBlock
    if (error) 
        // Handle the save error
     else 
        // Handle the successful save
    
];

【讨论】:

明白了。但是,我仍然需要保存mainContext,它是我的workerContext 的父级,对吧?我需要在主线程中调用该保存吗?现在我在使用performBlockAndWait保存workerContext后在主线程中做[self.mainContext save:&error];

以上是关于核心数据保存但不保存 - 专用队列跳转线程的主要内容,如果未能解决你的问题,请参考以下文章

与将 Web 应用程序保存在一个默认应用程序池中相比,拥有专用应用程序池的优缺点

基础篇:JAVA集合,面试专用

如何处理文件上传到专用图像服务器?

计算机专用英语词汇

MSMQ - 发送到远程专用队列啥都不做

DCL并非单例模式专用