使用 NSManagedObjectContext refreshObject 崩溃

Posted

技术标签:

【中文标题】使用 NSManagedObjectContext refreshObject 崩溃【英文标题】:Crash with NSManagedObjectContext refreshObject 【发布时间】:2016-04-10 18:55:26 【问题描述】:

我偶尔会收到来自用户的崩溃报告(我自己无法重现);这与 NSManagedObjectContext refreshObject 有关。崩溃消息是

“一个 NSManagedObjectContext 不能刷新其他上下文中的对象”

崩溃的代码在这里:

dispatch_async(self.filterMainQueue, ^
    NSArray *items = [Person getAllNonPrivatePersonsWithContext: self.backgroundContextImage];

    if (items.count) 

        for (Person *person in items) 

                [self.backgroundContextImage performBlockAndWait: ^
                    [person loadContactReferenceAndImage];

                    // crashes here
                    [self.backgroundContextImage refreshObject: person mergeChanges:NO];
                ];
                       
    
);

+ (NSArray *) getAllNonPrivatePersonsWithContext: (NSManagedObjectContext *) context

    __block NSArray *items = nil;

    [context performBlockAndWait: ^
        NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
        // Edit the entity name as appropriate.
        NSEntityDescription *entity = [NSEntityDescription entityForName:@"Person" inManagedObjectContext: context];
        [fetchRequest setEntity:entity];
        // Set the batch size to a suitable number.
        //[fetchRequest setFetchBatchSize: 500];
        [fetchRequest setPredicate: [NSPredicate predicateWithFormat: @"isContactPrivate == FALSE"]];
        [fetchRequest setReturnsObjectsAsFaults: FALSE];
        [fetchRequest setIncludesPendingChanges: FALSE];

        NSError *error = nil;
        items = [context executeFetchRequest:fetchRequest error:&error];

    ];
    return items;

我不太清楚为什么会崩溃。 backgroundContextImage 是使用NSPrivateQueueConcurrencyType 创建的,我确保在使用该上下文获取和访问托管对象时使用performBlockAndWaitfilterMainQueue 是一个串行队列,以帮助在后台线程而不是主线程中完成这项工作。

崩溃报告如下所示:

Exception Type:  SIGABRT
Exception Codes: #0 at 0x197fb7140
Crashed Thread:  3

Application Specific Information:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'An NSManagedObjectContext cannot refresh objects in other contexts.'

Last Exception Backtrace:
0   CoreFoundation                       0x0000000182a98f48 __exceptionPreprocess + 124
1   libobjc.A.dylib                      0x000000019764bf80 objc_exception_throw + 52
2   CoreData                             0x0000000182786898 -[NSManagedObjectContext refreshObject:mergeChanges:] + 1320
3   CJournal                             0x00000001001729c4 __75-[ContactsSyncController loadContactImagesAndLinksWithPermissionWithBlock:]_block_invoke_2 (ContactsSyncController.m:102)
4   CoreData                             0x00000001827dc900 developerSubmittedBlockToNSManagedObjectContextPerform + 192
5   libdispatch.dylib                    0x0000000197e696a8 _dispatch_client_callout + 12
6   libdispatch.dylib                    0x0000000197e74954 _dispatch_barrier_sync_f_invoke + 96
7   CoreData                             0x00000001827dc7e8 -[NSManagedObjectContext performBlockAndWait:] + 248
8   CJournal                             0x00000001001728bc __75-[ContactsSyncController loadContactImagesAndLinksWithPermissionWithBlock:]_block_invoke (ContactsSyncController.m:97)
9   libdispatch.dylib                    0x0000000197e696e8 _dispatch_call_block_and_release + 20
10  libdispatch.dylib                    0x0000000197e696a8 _dispatch_client_callout + 12
11  libdispatch.dylib                    0x0000000197e756ec _dispatch_queue_drain + 860
12  libdispatch.dylib                    0x0000000197e6d1ac _dispatch_queue_invoke + 460
13  libdispatch.dylib                    0x0000000197e696a8 _dispatch_client_callout + 12
14  libdispatch.dylib                    0x0000000197e77b40 _dispatch_root_queue_drain + 2136
15  libdispatch.dylib                    0x0000000197e772dc _dispatch_worker_thread3 + 108
16  libsystem_pthread.dylib              0x000000019807d470 _pthread_wqthread + 1088
17  libsystem_pthread.dylib              0x000000019807d020 start_wqthread + 0

有什么想法吗?

【问题讨论】:

您的 person 对象是否引用任何其他 CoreData 对象?您的 person 实例看起来很安全,但它是否以某种方式附加到不同上下文中的任何内容? 不,不这么认为...... Person 有关系,但在这种情况下它们应该作为故障加载。我会看更多来确认。但我的预感是另一个进程可能正在重置持久存储(以及主要的托管对象上下文)。这可能会解释问题吗? 谢谢。我应该如何修改我的代码以考虑到这一点?考虑到上面的主要代码在某处的后台队列中运行,并且在它下面更改了持久存储...如何防止refreshObject 崩溃? 我设法重现了这个问题......问题是在特殊情况下,应用程序中的其他东西会替换persistentStore并重置managedObjectContext,即使loadContactReferenceAndImagerefreshObject调用都在performBlockAndWait 内,backgroundContextImage 在中间被替换。有点出乎意料,因为我以为block会是原子操作,但可能是我理解错了 【参考方案1】:

问题在于,在特殊情况下,应用程序中的其他内容会替换 persistentStore 并重置 managedObjectContext,即使 loadContactReferenceAndImage 和 refreshObject 调用都在 performBlockAndWait 中。我想出的解决方案是确认managedObjectContext是否相同,然后才刷新对象:

       [self.backgroundContextImage performBlockAndWait: ^
              [person loadContactReferenceAndImage];

              // MOC could have been updated by another thread
              if (person.managedObjectContext == self.backgroundContextImage) 
                  [self.backgroundContextImage refreshObject: person mergeChanges:NO];
              

       ];

【讨论】:

以上是关于使用 NSManagedObjectContext refreshObject 崩溃的主要内容,如果未能解决你的问题,请参考以下文章

将块与 NSManagedObjectContext 一起使用 [重复]

使用 NSManagedObjectContext 清除整个核心数据

如何使用非 nil parentContext 创建 NSManagedObjectContext?

使用绑定时如何使子 NSManagedObjectContext 保持最新

创建 NSManagedObjectContext 以在线程中使用

获取 NSManagedObjectContext