调用核心数据操作时出现“未能及时更新场景”消息崩溃

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 帖子是一个单独的问题。

以上是关于调用核心数据操作时出现“未能及时更新场景”消息崩溃的主要内容,如果未能解决你的问题,请参考以下文章

删除后尝试保存时核心数据崩溃

下载大量图像时出现内存泄漏问题

使用核心数据时出现不一致错误

segueing时出现核心数据错误:无法在NSManagedObject类上调用指定的初始值设定项

核心数据 - 删除持久存储时出现死锁

启动我的应用程序时出现错误消息