【问题标题】:Initialising NSManagedObjectContext on a serial NSOperationQueue在串行 NSOperationQueue 上初始化 NSManagedObjectContext
【发布时间】:2014-01-05 19:55:41
【问题描述】:

我有一个NSOperationQueue,其中添加了一些NSBlockOperations,其中包括blockOperations A 和B。NSOperationQueuemaxConcurrencyOperationCount 为1。

blockOperation B,依赖于 A 的完成。在每个 blockOperations 中,我调用一个方法,该方法又调用另一个方法,该方法初始化一个新的NSManagedObjectContext(使用来自单例的 persistentStoreCoordinator),我用它来创建对象并将对象添加到 Core Data 数据库。前面提到的 A 和 B 中的第二个方法调用所调用的代码如下所示(每个方法略有不同):

NSManagedObjectContext *managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
managedObjectContext.persistentStoreCoordinator = [[CoreDataController sharedCoreDataController] persistantStoreCoordinator];

for (NSDictionary *articleDictionary in array) {

    if (![Article articleExistsWithIDInDictionary:articleDictionary inContext:managedObjectContext]) {

        [Article articleFromDictionary:articleDictionary inContext:managedObjectContext];
    }
}

[[CoreDataController sharedCoreDataController] saveContext:managedObjectContext];
// method ends.

saveContext: 代码如下所示:

NSError *error = nil;
if (managedObjectContext != nil) {
    if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {

        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    }
}

花了很多时间阅读有关核心数据并发、NSOperation 等的 Apple 文档,但我仍然不确定我使用 NSManagedObjectContext 所做的事情是否是线程安全的,并且通常被认为是可以的?一些澄清和/或表明我应该做不同的事情将不胜感激。如果您需要查看更多代码,请询问。

提前致谢。

【问题讨论】:

    标签: core-data nsmanagedobjectcontext nsoperation nsoperationqueue


    【解决方案1】:

    您所做的不是线程安全的。

    如果您决定为每个操作创建上下文,则最好使用约束并发类型:

    context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
    

    这样您就无需更改当前代码中的任何内容。

    如果您想通过NSPrivateQueueConcurrencyType 使用上下文,您必须通过performBlock:performBlockAndWait: 访问该上下文中的对象:

    [managedObjectContext performBlockAndWait:^{//wait is used as to not end the operation before this code is executed
        for (NSDictionary *articleDictionary in array) {
            if (![Article articleExistsWithIDInDictionary:articleDictionary
                                                inContext:managedObjectContext]) 
            {
                [Article articleFromDictionary:articleDictionary 
                                     inContext:managedObjectContext];
            }
        }
    }];
    

    在您的情况下,我可能会采用我的第一个解决方案。

    话虽如此,您可以简单地将“私有队列”上下文用作串行队列(只要您按照需要执行的顺序向其中添加块操作)。
    上下文performBlock: 方法将对该块进行排队,并针对添加该上下文以在后台执行的其他块串行执行它:

    //add this to your CoreDataController
    context = [[CoreDataController sharedCoreDataController] serialExecutionBGContext];
    [context performBlock:^{ //your block operation code1}];
    [context performBlock:^{ //your block operation code2}];
    

    这将在后台连续执行code1和code2。

    通过这种方式,您可以节省分配新上下文的开销,并且可能会受益于此上下文完成的缓存。
    您可能需要时不时地重置此上下文,这样它就不会因获取的对象而变得臃肿。

    【讨论】:

    • 嗨,丹,感谢您提供的信息丰富的回答。您能否澄清“重置此上下文”的含义? NSManagedObjectContext 上是否有reset 方法?另外,如果performBlock:是串行完成的,performBlock:performBlockAndWait:有什么区别?
    • 是的,在NSManagedObjectContext 上有一个reset 方法,使上下文不拥有与上下文相关的获取对象。当您不再需要在上下文中获取的现有数据但仍希望保留上下文时,这是一种回收内存的好方法。 performBlock 不是串行完成的,它将执行块串行排队,它异步执行而不阻塞当前线程(与 andWait 对应的线程不同)。一般来说,上下文相对于自身连续执行其执行的块。
    • 嗨,Dan,您说“上下文 performBlock: 方法将对块进行排队并串行执行它,因为其他块添加了该上下文以在后台执行:”但我在文档中找不到这个。您在哪里读到队列是串行的?一般来说,NSOperationQueue 默认不是串行的,我们必须写“queue.maxConcurrentOperationCount = 1”来使其串行。谢谢。
    • @superpuccio 我不确定它是否可以在官方文档中找到,但我能够找到this,它说明上下文使用串行队列see here
    【解决方案2】:

    上下文的问题在于它只能在单个线程中访问。设置 MaxConcurrencyOperationCount 并不能保证这一点。另一种方法是使上下文成为“线程”变量,将上下文存储在使用它的每个线程字典中。

    例如:

    + (NSManagedObjectContext*)managedObjectContext
    {
        NSMutableDictionary *threadDictionary = [[NSThread currentThread] threadDictionary];
        NSManagedObjectContext *context = [threadDictionary valueForKey:@"QpyManagedObjectContext"];
        if (context == nil) {
            @autoreleasepool {
                context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
                [context setStalenessInterval: 10.0];
                [context setMergePolicy:[[NSMergePolicy alloc]initWithMergeType:NSOverwriteMergePolicyType]];
    
                NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[Qpyd managedObjectModel]];
                [context setPersistentStoreCoordinator:coordinator];
    
                NSString *STORE_TYPE = NSSQLiteStoreType;
    
                NSString *path = [[NSProcessInfo processInfo] arguments][0];
                path = [path stringByDeletingPathExtension];
                NSURL *url = [NSURL fileURLWithPath:[path stringByAppendingPathExtension:@"sqlite"]];
    
                NSError *error;
                NSPersistentStore *newStore = [coordinator addPersistentStoreWithType:STORE_TYPE configuration:nil URL:url options:nil error:&error];
    
                if (newStore == nil) {
                    NSLog(@"Store Configuration Failure %@", ([error localizedDescription] != nil) ? [error localizedDescription] : @"Unknown Error");
                }
                [threadDictionary setObject:context forKey:@"QpyManagedObjectContext"];
            }
        }
        return context;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-29
      • 2019-07-26
      • 2010-11-30
      • 1970-01-01
      • 1970-01-01
      • 2016-02-11
      相关资源
      最近更新 更多