调用核心数据操作时出现“未能及时更新场景”消息崩溃
Posted
技术标签:
【中文标题】调用核心数据操作时出现“未能及时更新场景”消息崩溃【英文标题】:Crashing with "failed to scene-update in time" message while calling Core Data Operation 【发布时间】:2015-07-16 11:22:36 【问题描述】:我面临一个奇怪的问题“未能及时更新场景”。下载数据后,我将其存储到核心数据中。如果应用程序在前台,一切都会正常工作。仅当我在将数据存储到 Core Data 的过程中连续在状态、后台和前台之间切换应用程序时,我才会遇到此崩溃。在调试模式下不会发生这种崩溃。
我尝试将日志放在Core Data操作方法的不同阶段,我发现了应用程序实际崩溃的点。在 UI 中,我使用以下代码
DatabaseManagerClass *database = [[DatabaseManagerClass alloc]init];
[database deleteDataFromFormTable:nil];
[database insertDataInChecklistTable:[self.responseDictionary valueForKey:@"checklists"]];
[self performSelectorOnMainThread:@selector(mainThreadTask) withObject:nil waitUntilDone:YES];`
崩溃发生在方法“insertDataInChecklistTable”中。我检查了 insertDataInChecklistTable 工作的线程。它仅在主线程中。任何人都可以提出一些建议来解决这个问题。
提前致谢, 斯里。
带有执行块的更新代码
-(void)insertDataInFormColumnsTable:(NSMutableArray*)responseArray withForm:(Form*)form
AppDelegate objAppDelegate = (AppDelegate)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *moc = [objAppDelegate managedObjectContext];
NSManagedObjectContext *bgMOC = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[bgMOC performBlock:^
for (int count=0; count<[responseArray count]; count++)
NSDictionary *columnDict = [responseArray objectAtIndex:count];
Form_Column *formColumn = [NSEntityDescription insertNewObjectForEntityForName:@"Form_Column" inManagedObjectContext:moc];
if ([columnDict valueForKey:FORMULA]!=[NSNull null]) //Added by mritunjay
formColumn.formula = [columnDict valueForKey:FORMULA];
else
formColumn.formula=nil;
if ([columnDict valueForKey:ASP_EDITABLE]!=[NSNull null])
formColumn.asp_editable = [columnDict valueForKey:ASP_EDITABLE];
else
formColumn.asp_editable=nil;
if ([columnDict valueForKey:CM_EDITABLE]!=[NSNull null])
formColumn.cm_editable = [columnDict valueForKey:CM_EDITABLE];
else
formColumn.cm_editable=nil;
if ([columnDict valueForKey:IM_EDITABLE]!=[NSNull null])
formColumn.im_editable = [columnDict valueForKey:IM_EDITABLE];
else
formColumn.im_editable=nil;
if ([columnDict valueForKey:SP_EDITABLE]!=[NSNull null])
formColumn.sp_editable = [columnDict valueForKey:SP_EDITABLE];
else
formColumn.sp_editable=nil;
if ([columnDict valueForKey:ASP_EDITABLE]!=[NSNull null])
formColumn.dialog_row = [columnDict valueForKey:ASP_EDITABLE];
else
formColumn.dialog_row=nil;
if ([columnDict valueForKey:INDEX]!=[NSNull null])
formColumn.index = [columnDict valueForKey:INDEX];
else
formColumn.index=nil;
if ([columnDict valueForKey:WEB_ONLY]!=[NSNull null])
formColumn.web_only = [columnDict valueForKey:WEB_ONLY];
else
formColumn.web_only=nil;
if ([columnDict valueForKey:LABEL]!=[NSNull null])
formColumn.label = [columnDict valueForKey:LABEL];
else
formColumn.label=nil;
formColumn.form_id = form.form_id;
if ([columnDict valueForKey:ID]!=[NSNull null])
formColumn.column_id=[columnDict valueForKey:ID];
else
formColumn.column_id=nil;
if ([columnDict valueForKey:TYPE]!=[NSNull null])
formColumn.type=[columnDict valueForKey:TYPE];
else
formColumn.type=nil;
NSArray *selectionArray = [columnDict valueForKey:SELECTIONS];
if ([selectionArray count])
for (int count = 0; count < [selectionArray count]; count++)
Form_Sub_Columns *formSubColumns = [NSEntityDescription insertNewObjectForEntityForName:@"Form_Sub_Columns" inManagedObjectContext:moc];
NSDictionary *subColumnDict = [selectionArray objectAtIndex:count];
if ([subColumnDict valueForKey:ID]!=[NSNull null])
formSubColumns.column_id=[subColumnDict valueForKey:ID];
else
formSubColumns.column_id=nil;
if ([subColumnDict valueForKey:LABEL]!=[NSNull null])
formSubColumns.label=[subColumnDict valueForKey:LABEL];
else
formSubColumns.label=nil;
if ([subColumnDict valueForKey:REQUIRES_REMARKS]!=[NSNull null])
formSubColumns.requires_remarks=[subColumnDict valueForKey:REQUIRES_REMARKS];
else
formSubColumns.requires_remarks=nil;
if ([subColumnDict valueForKey:VALUE]!=[NSNull null])
formSubColumns.value=[subColumnDict valueForKey:VALUE];
else
formSubColumns.value=nil;
if ([subColumnDict valueForKey:DESCRIPTION]!=[NSNull null])
formSubColumns.description_name=[subColumnDict valueForKey:DESCRIPTION];
else
formSubColumns.description_name=nil;
if ([subColumnDict valueForKey:SELECTION_TYPE_ID]!=[NSNull null])
formSubColumns.selection_type_id=[subColumnDict valueForKey:SELECTION_TYPE_ID];
else
formSubColumns.value=nil;
[formColumn addSun_columnsObject:formSubColumns];
[formColumn addFormObject:form];
[form addColumnsObject:formColumn];
if([form.enable_attachment boolValue])
Form_Column *formColumn = [NSEntityDescription insertNewObjectForEntityForName:@"Form_Column" inManagedObjectContext:moc];
formColumn.label = @"Add Attachments";
formColumn.form_id = form.form_id;
formColumn.index = [NSNumber numberWithInt:998];
formColumn.column_id=[NSNumber numberWithInt:998]; // hardcoded if Attachment is Enabled.
formColumn.web_only = [NSNumber numberWithInt:0];
formColumn.type=[NSNumber numberWithInt:-1];
[form addColumnsObject:formColumn];
if ([[NSThread currentThread] isMainThread])
NSLog(@"Main Thread inside block");
else
NSLog(@"Not main thread inside block");
NSError *error;
if(![bgMOC save:&error])
NSLog(@"Error %@", [error localizedDescription]);
];
【问题讨论】:
如果你把实际的堆栈跟踪放在你的问题中会很有帮助,这样我们就可以看到什么时候被调用了。我还建议 NOT 使用-performSelectorOnMainThread:
,因为它隐藏了它之前的堆栈跟踪(它基本上被包裹在一个 try/catch 中)。 dispatch_async
是将某些东西放到主线程上的更好解决方案。但总的来说,如果您将数据写入 Core Data 它不应该在主线程上。您阻塞主线程很可能导致此崩溃。
马库斯,感谢您回复我。我理解您所说的 performSelectorOnMainThread:调用选择器方法时不会发生实际崩溃。选择器方法调用前的行,执行数据库操作的方法。崩溃是在这种方法中发生的。所以我不应该在主线程中调用数据库操作。最后,我将日志放入 db 操作方法中,发现实际崩溃发生在 Coredata 的 addObject 方法中。真的很难找到解决方案。
您的崩溃正在发生,因为您阻塞主线程的时间过长并且操作系统正在杀死您的应用程序。在-insertDataInChecklistTable
上放一个断点,看看你在哪个线程上。你在主线吗?如果没有,请发布该方法的代码。
Marcus,InsertDataInChecklistTable 方法正在 MainThread 中调用。
【参考方案1】:
在主线程/UI 线程上处理数据总是是个坏主意,会导致这样的问题。
您的-insertDataIntoChecklistTable
需要在后台队列中运行,这意味着您应该:
-
创建第二个
NSManagedObjectContext
,即NSPrivateQueueConcurrencyType
将其与您的主要上下文相关联
使用-performBlock:
处理针对NSManagedObjectContext
的数据
处理完成后保存
这将使您的数据处理脱离主队列。永远不要在主队列上处理数据。
【讨论】:
亲爱的马库斯,这是有道理的,我希望这些提示能解决我的问题。我会尝试并让你知道。现在,在 AppDelegate 中初始化的 NSManagedObjectContext 到处都在使用 coredata 操作。那么我应该为每个 coredata 操作创建新的 NSManagedObjectContext 实例吗?非常感谢您的建议。 Marcus,我已经通过添加带有执行块的更新代码来编辑我的帖子。我尝试了不同的方式 performBlock 以及 PerformBlockAndWait。 performBlockAndWait 以正常方式工作,但在进入前台和后台时的崩溃仍在发生。这意味着 performBlockAndWait 正在阻塞主线程。在 performBlock 的情况下,我收到一个异常“*** Terminating app due to unaught exception 'NSGenericException', reason: '*** Collection <__nscfset:> was mutated while being enumerated.'”。但我确保该块在不同的线程中运行。-performBlockAndWait:
确实会阻止。这就是AndWait
部分的含义:) 得到“枚举时发生突变”的错误不是核心数据错误,这是一个正常的编程问题,因为您正在枚举一个集合并且您正在改变您正在枚举的集合。这总是很糟糕,并且与这篇 SO 帖子是一个单独的问题。以上是关于调用核心数据操作时出现“未能及时更新场景”消息崩溃的主要内容,如果未能解决你的问题,请参考以下文章