UI 在执行 NSBatchDeleteRequest 时冻结

Posted

技术标签:

【中文标题】UI 在执行 NSBatchDeleteRequest 时冻结【英文标题】:UI freezes on executing NSBatchDeleteRequest 【发布时间】:2017-11-27 17:37:22 【问题描述】:
NSFetchRequest *request = [Report fetchRequest];
request.predicate = [NSPredicate predicateWithFormat: @"reportId IN %@",self.selectedRowsInEditMode];

NSBatchDeleteRequest *deleteDetailsRequest = [[NSBatchDeleteRequest alloc] initWithFetchRequest:request];
deleteDetailsRequest.resultType = NSBatchDeleteResultTypeStatusOnly;
[self.managedObjectContext executeRequest:deleteDetailsRequest error:&errorExecuteRequest];

if (!errorExecuteRequest) 
    NSError *deleteSaveError;
    BOOL saveSuccessful = [self.managedObjectContext save:&deleteSaveError];
    [self.tableView removeLoadingActivityView];

这是我试图一次删除多个报告托管对象的代码,但是当调用 executeRequest 时,UI 会冻结一段时间然后删除。但是 NSBatchDeleteRequest 在删除方面应该非常快。我尝试了多次修改以使其正常工作,但没有成功。 我不确定我在这里做错了什么。所以任何人都可以建议让它在没有 UI 故障的情况下工作。

【问题讨论】:

能否启用 SQLDebug 并发布控制台输出。它应该显示有关正在处理以完成批量删除的 SQL 请求的一些详细信息。 您是否知道您的数据库有多大以及您要删除的对象大致有多少?您的批量删除请求也在主线程上执行吗? (我想是的,如果它冻结了 UI) @frankodwyer 我之前在主队列上有代码,然后我把它移到了一个单独的后台队列。这样就解决了,但我担心 NSBatchDeleteRequest 需要一些时间来删除甚至删除单个实体的请求。该实体有一个具有无效规则的关系对象。即使我有一个单一的关系 obj 那么也需要时间。 @pbasdf 无法发布数据库的控制台日志,因为我无法共享数据信息。 @pbasdf 我只能说我在这里使用的实体对象有几个关系。但即使我在其中一个关系中只有一个关系对象,也需要时间。但我想大致了解 NSBathcDeleteRequest 的行为如何? 【参考方案1】:

希望下面的代码对你有所帮助!!

我最终添加了以下内容来处理批量删除后的上下文。

[self.managedObjectContext refreshAllObjects];

// Initialize Fetch Request
NSFetchRequest *fetchRequest = [Report fetchRequest];

// Add Sort Descriptors
fetchRequest.predicate = [NSPredicate predicateWithFormat: @"reportId IN %@",self.selectedRowsInEditMode];

// Initialize Asynchronous Fetch Request
NSAsynchronousFetchRequest *asynchronousFetchRequest = [[NSAsynchronousFetchRequest alloc] initWithFetchRequest:fetchRequest completionBlock:^(NSAsynchronousFetchResult *result) 
    dispatch_async(dispatch_get_main_queue(), ^
        // Process Asynchronous Fetch Result
        [weakSelf processAsynchronousFetchResult:result];
    );
];

// Execute Asynchronous Fetch Request
[self.managedObjectContext performBlock:^
    // Execute Asynchronous Fetch Request
    NSError *asynchronousFetchRequestError = nil;
    NSAsynchronousFetchResult *asynchronousFetchResult = (NSAsynchronousFetchResult *)[weakSelf.managedObjectContext executeRequest:asynchronousFetchRequest error:&asynchronousFetchRequestError];

    if (asynchronousFetchRequestError) 
        NSLog(@"Unable to execute asynchronous fetch result.");
        NSLog(@"%@, %@", asynchronousFetchRequestError, asynchronousFetchRequestError.localizedDescription);
    
];

或者使用下面你使用全局队列在后台运行东西和主队列来更新 UI

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void)
    //Background Thread
    dispatch_async(dispatch_get_main_queue(), ^(void)
        //Run UI Updates
    );
);

【讨论】:

感谢您提供的信息,但我想要一个解决方案或建议,为什么批量删除需要这么多时间。想知道我是否在批量删除花费这么多时间时做错了什么。异步获取请求将是最后一种方法,并为用户提供有关删除正在进行中的指示符。但想知道是否有任何方法可以防止 BatchDeleteRequest 花费这么多时间 使用这一行代码 [self.managedObjectContext refreshAllObjects];希望能帮到你! 在执行 batchdeleterequest 之前刷新? 是在执行批量删除请求之前使用 您提到“我最终添加了以下内容以处理批量删除后的上下文。”那么你的意思是在调用 execute:(BatchRequest) 之后添加上面提到的代码吗?如果是这样,那么我在调用 execute:(BatchRequest) 后不会遇到问题,但问题是 execute:(BatchRequest) 本身需要很多时间。【参考方案2】:

您可以创建一个异步获取请求,同步可以冻结 UI。此链接Asynchronous Fetch Request 是 Objective C 中的一个教程,它解释了如何。

【讨论】:

以上是关于UI 在执行 NSBatchDeleteRequest 时冻结的主要内容,如果未能解决你的问题,请参考以下文章

如何让工作线程在主 UI 线程上执行回调?

UI 在执行 NSBatchDeleteRequest 时冻结

在 MFC 中,可以在工作线程中执行 UI 相关操作(绘制绘图)吗?

消息循环是不是在 UI 线程上执行?

UI 窗口关闭时执行命令 |玛雅/蟒蛇

为啥在 setContentView() 之后 UI 不加载,而是在 onCreate() 执行之后加载?