【问题标题】:CoreData Fetch Crashes if used with dispatch_async如果与 dispatch_async 一起使用,CoreData Fetch 会崩溃
【发布时间】:2012-03-31 20:03:23
【问题描述】:

如果我像这样在viewDidLoad 中执行提取,我有一个核心数据应用程序运行时不会崩溃:

- (void) performCoreDataFetch {
   NSError *error;
    if (![[self fetchedResultsController] performFetch:&error]) {
        exit(-1);  // Fail
    }
 }

- (void)viewDidLoad {
    [super viewDidLoad];
    [self performCoreDataFetch];
}

上述执行获取方式的唯一问题是,如果要返回的数据很大,它会冻结应用程序几秒钟(但确实返回正确的结果而不会每次都崩溃),所以为了避免这种情况我决定使用 dispatch_async(代码如下)并在其中调用[self performCoreDataFetch]

但是如果我在 dispatch_sync within viewDidLoad 中运行同样的 [self performCoreDataFetch],如下所示:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self performCoreDataFetch];
        dispatch_async(dispatch_get_main_queue(), ^{
           [self.tableView reloadData];
        });
    });

在 dispatch_async 中调用 [self performCoreDataFetch] 会导致应用随机崩溃,并说“-[NSFetchRequest fetchLimit]: message sent to deallocated instance

我的 fetchedResultsController 方法如下所示:

- (NSFetchedResultsController *)fetchedResultsController {
    if (fetchedResultsController != nil) {
        return  fetchedResultsController;
    }

    // Create and configure a fetch request with the Organization entity
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    request.entity = [NSEntityDescription entityForName:@"Organization" inManagedObjectContext:managedObjectContext];
    request.fetchBatchSize = 20;

    // create sortDescriptor array
    NSSortDescriptor *nameDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES selector:@selector(caseInsensitiveCompare:)];
    NSArray *sortDescriptorArray = [NSArray arrayWithObjects:nameDescriptor, nil];
    request.sortDescriptors = sortDescriptorArray;

    NSPredicate *predicate = nil;

    predicate = [NSPredicate predicateWithFormat:@"state LIKE %@", filterByState];
    [request setPredicate:predicate];

    // Create and initialize the fetchedResultsController
    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc ] initWithFetchRequest:request managedObjectContext:managedObjectContext sectionNameKeyPath:@"uppercaseFirstLetterOfName" cacheName:nil];

    self.fetchedResultsController = aFetchedResultsController;
    fetchedResultsController.delegate = self;

    // Memory management
    filterByState = nil;
//    [sortDescriptorArray release];
    [nameDescriptor release];
//    [predicate release];
    [request release];
    [aFetchedResultsController release];

    return fetchedResultsController;

}

【问题讨论】:

  • 你想完成什么?您不必自己调用performFetch 方法...
  • 如果没有 performFetch,我不知道执行核心数据获取的另一种方法。
  • 你想完成什么?如果你懒惰地初始化 fetched results 控制器,那么将在那里完成获取,而无需你自己调用performFetch。再说一次,你想用 fetch 做什么?
  • 这是我想要做的:如果你查看我的 fetchedResultsController 方法,有一个谓词参数“filterByState”。这个变量通过不同的 tableViewController 传递给这个 UITableViewController。基于该谓词,此 fetchedResultsController 返回的数据集每次都不同。如果这仍然不清楚,请告诉我。

标签: core-data nsfetchedresultscontroller objective-c-blocks grand-central-dispatch


【解决方案1】:

如果您为 fetchedResultsController 执行提取,核心数据不是线程安全的。这是有道理的,因为 fetchedResultsController 是 UI 的数据源。不要执行提取,而是将您的 fetchedResultsController 设置为 nil 并重新加载您的 tableView。

【讨论】:

  • 你是说在我的 nsfetchedResultsController 方法中,我应该总是从将 fetchedResultsController 设置为 nil 开始?
  • 没有。当您需要更新表格时,您将其设置为nil。表更新将自动调用您的 fetchedResultsController 方法,该方法将懒惰地创建一个新方法。 (看看方法,看看它是如何工作的。)
【解决方案2】:

核心数据不是线程保存的。更准确地说, NSManagedObjectContext 是不保存的。所有的 NSManagedObject 都属于一个特定的 NSManagedObjectContext 并且它们是不可互换的。

在 IOS 5 之前你需要把非常非常复杂的方法。基本上每个线程都需要它自己的 NSManagedContext

IOS5之后,你可以这样做:

__managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];

那你就可以了

[__managedObjectContext performBlock ^{
    //Some really long operation
}]

在任何不是主线程的线程上。

这将在不同的线程上执行,但是以线程保存方式。基本上,核心数据会将您的操作放入队列中,并且会为每个操作一个一个地锁定 managedObjectContext。

【讨论】:

    猜你喜欢
    • 2021-05-02
    • 2021-12-30
    • 2014-07-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-28
    相关资源
    最近更新 更多