Core Data 后台处理,保存不推送到主上下文

Posted

技术标签:

【中文标题】Core Data 后台处理,保存不推送到主上下文【英文标题】:Core Data background processing, save is not pushed to main context 【发布时间】:2013-02-12 09:47:16 【问题描述】:

我在某些 Core Data 后台处理方面遇到了一些问题。当我在后台上下文中保存时,它似乎并没有将保存推到主上下文中。在调试代码时,我注意到正在执行保存操作的后台线程似乎已停止(?)这种行为导致我获取过时的对象。

来自保存的堆栈跟踪:

Thread 29, Queue : NSManagedObjectContext Queue
#0  0x9a5cf80e in semaphore_wait_trap ()
#1  0x02216f08 in _dispatch_thread_semaphore_wait ()
#2  0x02214b3a in _dispatch_barrier_sync_f_slow ()
#3  0x02214a5c in dispatch_barrier_sync_f ()
#4  0x01dfe03b in _perform ()
#5  0x01dfde9e in -[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:] ()
#6  0x01ddb33c in -[NSManagedObjectContext save:] ()
#7  0x00096213 in __45-[CoreDataHelper saveInManagedObjectContext:]_block_invoke_0 at /Users/peterwarbo/Documents/Projects/MessagePlanr/MessagePlanr/CoreDataHelper.m:307
#8  0x01e734b3 in developerSubmittedBlockToNSManagedObjectContextPerform_privateasync ()

保存方法:

- (void)saveInManagedObjectContext:(NSManagedObjectContext *)context 

    if (context == nil) 

        // Use default MOC
        context = self.managedObjectContext;

        NSError *error = nil;

        if (context != nil)
        
            if ([context hasChanges] && ![context save:&error])
            
                /*
                 Replace this implementation with code to handle the error appropriately.

                 abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
                 */
                DLog(@"Unresolved error %@, %@", error, [error userInfo]);
                abort();
            
        

     else 

        // First save (child) context
        [context performBlock:^

            NSError *error = nil;

            if ([context hasChanges] && ![context save:&error])
            
                /*
                 Replace this implementation with code to handle the error appropriately.

                 abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
                 */
                DLog(@"Unresolved error %@, %@", error, [error userInfo]);
                abort();
            
        ];


        // Then save parent context
        [self.managedObjectContext performBlock:^

            NSError *error = nil;

            if ([self.managedObjectContext hasChanges] && ![self.managedObjectContext save:&error]) 

                /*
                 Replace this implementation with code to handle the error appropriately.

                 abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
                 */
                DLog(@"Unresolved error %@, %@", error, [error userInfo]);
                abort();
            
        ];
    

这是保存的方法,Reminder是一个NSManagedObject,当操作完成时我调用一个完成块。但是在完成块中,当获取一些 NSManagedObjects 时,它们还没有更新(我猜是因为保存暂停?)

- (void)checkOverdueRemindersInBackgroundWithCompletionBlock:(void (^)(NSInteger overdueCount, NSArray *reminders))block 

    DLogName()

    // Creating a new MOC for thread safety
    NSManagedObjectContext *syncContext = [self threadedManagedObjectContext];

    [syncContext performBlock:^

        NSArray *reminders = [self fetchEntity:APReminderEntity predicate:nil andSortDescriptors:nil inManagedObjectContext:syncContext];

        NSInteger overdueCount = 0;

        for (Reminder *reminder in reminders) 

            [reminder checkOverdue]; // Checks if object is overdue and sets a flag if it is

            [self saveInManagedObjectContext:syncContext];

            if (reminder.status.intValue == RMReminderStatusOverdue) 

                overdueCount++;
            

        

        block(overdueCount, reminders);
    ];

threadedManagedObjectContext 方法:

- (NSManagedObjectContext *)threadedManagedObjectContext 

    NSManagedObjectContext *threadedMoc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    threadedMoc.parentContext = self.managedObjectContext; //self.managedObjectContext is of type NSMainQueueConcurrencyType

    return threadedMoc;

【问题讨论】:

在您的saveInManagedObjectContext 方法中,尝试将您的父上下文保存在子上下文performBlock 方法和if 语句中 @Yaman 感谢您的建议,但这不是问题所在。在对performBlock* API 进行了一些调查之后,我自己发现了这个问题。 很高兴它对你有用。你能用你找到的解决方案更新你的帖子吗?它可以帮助其他有同样问题的人。 @Yaman 我回答了我自己的问题:) 【参考方案1】:

出现问题是因为我正在使用 performBlock: 异步保存,它会立即返回,因此当它被返回并且我的完成块被调用时,保存可能不会被提交。

所以这个问题的答案是在performBlockAndWait:内运行后台保存进程

现在又出现了一个问题,使用performBlockAndWait:有什么缺点吗?

【讨论】:

以上是关于Core Data 后台处理,保存不推送到主上下文的主要内容,如果未能解决你的问题,请参考以下文章

在后台线程上安全保存 Core Data 托管对象上下文的正确方法?

应用重新启动时数据不会在 Core Data 中持久化

Core Data 托管对象上下文线程同步

当应用程序后台运行时,Core Data 无法通过区域监控保存上下文

Core Data 使用多个上下文中的新对象从后台线程订购一对多关系保存

多上下文麻烦。无法在 Core Data 中创建两个队列(主队列和私有队列)保存数据