允许子NSManagedObjectContext在其父上下文保存时进行获取的设置 [英] Setup that allows a child NSManagedObjectContext to Fetch when it's parent context is Saving

查看:93
本文介绍了允许子NSManagedObjectContext在其父上下文保存时进行获取的设置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

一次保存大量数据非常慢.

在我的应用中,有一个私有队列 NSManagedObjectContext作为父级,可以直接与NSPersistentStoreCoordinator进行对话以保存数据. UI的NSTreeController使用子主队列上下文.

In my app there's a private-queue NSManagedObjectContext as the parent that talks to the NSPersistentStoreCoordinator directly to save data. A child main-queue context is consumed by a NSTreeController for the UI(NSOutlineView).

(我的目标是防止发生沙滩球.目前,我仅通过在应用程序处于非活动状态时保存数据来解决此问题.但是由于计划删除的数据不会删除但它们仍可能会出现在获取结果中.这是我要解决的另一个问题.)

(My goal was to prevent any occurence of the beach ball. Currently I remedy the problem by only saving data when the app goes inactive. But since the data that are planed to be deleted are not deleted yet, they may still come up in a fetch result. That's another problem I'm trying to solve.)

子父队列上下文仅在父上下文忙于保存时才等待获取.

  • 核心数据:父上下文块孩子这也许本质上是相同的问题.我注意到答案说安装程序本质上是错误的.然后,我想知道是否有一种方法可以同时对Core Data进行写入和读取?

  • Core Data: parent context blocks child This perhaps is essentially the same question. I've noticed the answer says the setup is intrinsically wrong. Then I wonder is there a way to do the writing and reading at the same time with Core Data?

父/子NSManagedObjectContext的正确实现常见问题问题缺少有见解的答案(对不起...).

Correct implementation of parent/child NSManagedObjectContext A commonly asked question lacks answers with insights (sorry...).

What is the most efficient way to delete a large number (10.000+) objects in Core Data? If the objects can't be designated by a NSPredicate, we still need to rely on the traditional delete() (and maybe the Cascade delete rule) Also, it doesn't eliminate the blocking-the-UI issue.

当我有更多发现时,我将更新此问题.

I will update this question when I have more findings.

推荐答案

我猜您正在为OS X/macOS(NSTreeControllerNSOutlineView)开发.我没有使用macOS的经验-我是为iOS开发的-因此,在阅读我的回复时,您可能需要考虑到这一点.

I'm guessing you're developing for OS X / macOS (NSTreeController & NSOutlineView). I've no experience with macOS - I develop for iOS - so you might need to take that into account when you're reading my response.

我尚未切换到快速状态-我的代码显然是Objective-C ...

I've not yet made the switch to swift - my code is, perhaps obviously, Objective-C...

我将从准备核心数据堆栈开始.

I'll start with how I prepare the Core Data stack.

我在头文件中设置了两个公共属性:

I set up two public properties in the header file:

@property (nonatomic, strong) NSManagedObjectContext *mocPrivate;
@property (nonatomic, strong) NSManagedObjectContext *mocMain;

尽管这是不必要的,但我也更愿意为我的Core Data对象设置私有属性,例如:

Although this is unnecessary, I also prefer to set up private properties for my Core Data objects, including, for example:

@property (nonatomic, strong) NSPersistentStoreCoordinator *persistentStoreCoordinator;

一旦我指向我的模型URL,建立了我的受管理对象模型NSManagedObjectModel,指向了我的NSPersistentStore的商店URL,并建立了我的持久性商店协调器NSPersistentStoreCoordinator(PSC),就设置了两个管理对象上下文(MOC).

Once I've pointed to my model URL, established my managed object model NSManagedObjectModel, pointed to my store URL for my NSPersistentStore and established my persistent store coordinator NSPersistentStoreCoordinator (PSC), I set up my two managed object contexts (MOC).

在完成上述段落的代码后,在构建"我的Core Data堆栈的方法中,然后包括以下内容...

Within the method to "build" my Core Data stack, after I've completed the code per the above paragraph, I then include the following...

if (!self.mocPrivate) {
    self.mocPrivate = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    [self.mocPrivate setPersistentStoreCoordinator:self.persistentStoreCoordinator];
} else {
    // report to console the use of existing MOC
}

if (!self.mocMain) {
    self.mocMain = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    [self.mocMain setParentContext:self.mocPrivate];
} else {
    // report to console the use of existing MOC
}

(我通常在此代码中包括几行NSLog行,以向控制台报告,但在此我将其排除在外以保持代码整洁.)

(I usually include a few NSLog lines in this code to report to my console but I've excluded that here to keep the code clean.)

请注意此代码的两个重要方面...

Note two important aspects to this code...

  • 设置专用队列MOC与PSC交互;和
  • 将主队列MOC设置为专用队列MOC的子代.

为什么要这样做?首先,让我们重点强调几个要点:

Why is this done? First let's highlight a couple of important points:

  • 保存到内存相对较快;和
  • 保存到光盘相对较慢.

专用队列与主队列异步.用户界面(UI)在主队列上运行.专用队列在后台"的单独线程上运行,以维护上下文并与PSC协调数据持久性,由Core Data和iOS完美管理.主队列通过UI在主线程上运行.

The private queue is asynchronous to the main queue. The User Interface (UI) operates on the main queue. The private queue operates on a separate thread "in the background" working to maintain context and coordinate data persistence with the PSC, perfectly managed by Core Data and iOS. The main queue operates on the main thread with the UI.

用另一种方式写...

Written a different way...

  • 在专用队列中完成了繁重的工作,完成了对PSC(保存到磁盘)的不规则数据(由Core Data管理)的持久性;和
  • 完成对MOC的常规(由开发人员管理)数据持久性(保存到内存)的轻型工作在主队列中完成.

从理论上讲,这应该确保您的UI永远不会被阻塞.

In theory this should ensure your UI is never blocked.

但是此解决方案还有更多.我们如何管理保存"过程很重要...

But there is more to this solution. How we manage the "save" process is important...

我写了一个公共方法:

- (void)saveContextAndWait:(BOOL)wait;

我从需要持久化数据的任何类中调用此方法.此公共方法的代码:

I call this method from any class that needs to persist data. The code for this public method:

- (void)saveContextAndWait:(BOOL)wait {
    // 1. First
    if ([self.mocMain hasChanges]) {
    // 2. Second
        [self.mocMain performBlockAndWait:^{
            NSError __autoreleasing *error;
            BOOL success;
            if (!(success = [self.mocMain save:&error])) {
                // error handling
            } else {
                // report success to the console
            }
        }];
    } else {
        NSLog(@"%@ - %@ - CORE DATA - reports no changes to managedObjectContext MAIN_", NSStringFromClass(self.class), NSStringFromSelector(_cmd));
    }

    // 3. Third
    void (^savePrivate) (void) = ^{
        NSError __autoreleasing *error;
        BOOL success;
        if (!(success = [self.mocPrivate save:&error])) {
                // error handling
            } else {
                // report success to the console
        }
    };

    // 4. Fourth
    if ([self.mocPrivate hasChanges]) {
    // 5. Fifth
        if (wait) {
            [self.mocPrivate performBlockAndWait:savePrivate];
        } else {
            [self.mocPrivate performBlock:savePrivate];
        }
    } else {
        NSLog(@"%@ - %@ - CORE DATA - reports no changes to managedObjectContext PRIVATE_", NSStringFromClass(self.class), NSStringFromSelector(_cmd));
    }
}

因此,我将通过这一过程来解释正在发生的事情.

So I'll work through this to explain what is happening.

我为开发人员提供了保存和等待(阻止)的选项,并且根据开发人员对方法saveContextAndWait:wait的使用,专用队列MOC使用以下任一方法保存":

I offer the developer the option to save and wait (block), and depending on the developer's use of the method saveContextAndWait:wait, the private queue MOC "saves" using either:

  • performBlockAndWait方法(开发人员使用wait = TRUEYES调用方法);或
  • performBlock方法(开发人员使用wait = FALSENO调用方法).
  • the performBlockAndWait method (developer calls method with wait = TRUE or YES); or
  • the performBlock method (developer calls method with wait = FALSE or NO).

首先,该方法检查主队列MOC是否有任何更改.除非必须,否则不要做任何工作!

First, the method checks whether there are any changes to the main queue MOC. Let's not do any work unless we have to!

第二,该方法完成对主队列MOC上的performBlockAndWait的(同步)调用.这将在代码块中执行对save方法的调用,并等待完成,然后再允许代码继续.请记住,这是对小型数据集的常规保存".这里不需要调用performBlock的(异步)选项,并且实际上会破坏该方法的有效性,正如我在学习在代码中实现该方法时遇到的那样(由于save调用而导致无法持久存储数据) save在专用队列MOC上完成后,尝试完成的主队列MOC上的数据).

Second, the method completes a (synchronous) call to performBlockAndWait on the main queue MOC. This performs the call to save method in a code block and waits for completion before allowing the code to continue. Remember this is for regular "saves" of small data sets. The (asynchronous) option to call performBlock is not required here and in fact will derail the effectiveness of the method, as I experienced when I was learning to implement this in my code (failure to persist data due to the save call on the main queue MOC attempting to complete after completion of the save on the private queue MOC).

第三,我们在一个包含保存私有队列MOC的代码的块中写一个小块.

Third, we write a little block within a block that contains the code to save the private queue MOC.

第四,该方法检查专用队列MOC是否有任何更改.这可能是不必要的,但将其包含在此处是无害的.

Fourth, the method checks whether there are any changes to the private queue MOC. This may be unnecessary but it is harmless to include here.

第五,根据开发人员选择实施的选项(wait = YESNO),该方法在块内的块上调用performBlockAndWaitperformBlock(在第三).

Fifth, depending on the option the developer chooses to implement (wait = YES or NO) the method calls either performBlockAndWait or performBlock on the block within a block (under third above).

在这最后一步中,无论实现方式(wait = YES NO),从私有队列MOC到PSC的将数据持久存储到磁盘的功能,在与主线程异步的线程上抽象为专用队列.从理论上讲,通过PSC进行保存到磁盘"可以花费任意多的时间,因为它与主线程无关. AND,因为专用队列MOC将所有数据都存储在内存中,所以主队列MOC是更改的完全自动通知,因为它是专用队列MOC的子级.

In this last step, regardless of the implementation (wait = YES or NO), the function of persisting data to disc, from the private queue MOC to the PSC, is abstracted to the private queue on an asynchronous thread to the main thread. In theory the "save to disc" via the PSC can take as long as it likes because it has nothing to do with the main thread. AND because the private queue MOC has all the data in memory, the main queue MOC is fully and automatically informed of the changes because it is the child of the private queue MOC.

如果您将大量数据导入到应用程序中(我正在努力实现),那么将这些数据导入私有队列MOC是有意义的.

If you import large volumes of data into app, something I am currently working on implementing, then it makes sense to import this data into the private queue MOC.

私有队列MOC在这里做两件事:

The private queue MOC does two things here:

  • 它与PSC协调数据持久性(到磁盘);
  • 由于它是主队列MOC(在内存中)的父级,因此将通知主队列MOC私有队列MOC中的数据更改,并且合并由Core Data管理;

最后,我使用NSFetchedResultsController(FRC)来管理我的数据提取,这些数据提取都是针对主队列MOC完成的.这样可以保持数据层次结构.当在任一上下文中对数据集进行更改时,FRC都会更新视图.

Finally, I use NSFetchedResultsController (FRC) to manage my data fetches, which are all completed against the main queue MOC. This maintains data hierarchy. As changes are made to the data sets in either context, the FRC updates the view.

此解决方案很简单! (一旦我花了几周时间围绕它绞尽脑汁,又花了几周时间完善我的代码.)

This solution is simple! (Once I spent weeks wrangling my head around it and another few weeks refining my code.)

不需要监视MOC的合并或其他更改的通知.核心数据和iOS在后台处理一切.

There is no requirement to monitor notifications for merges or other changes to MOC. Core Data and iOS handle everything in the background.

因此,如果这对您不起作用-让我知道-一年多以前写这段代码时,我可能已经排除或忽略了某些东西.

So if this doesn't work for you - let me know - I may have excluded or overlooked something as I wrote this code well over a year ago.

这篇关于允许子NSManagedObjectContext在其父上下文保存时进行获取的设置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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