如果与dispatch_async一起使用,则CoreData Fetch崩溃

时间:2012-03-31 20:03:23

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

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

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

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

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

但如果我在viewDidLoad 中的dispatch_sync 中运行同样的[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;

}

2 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:1)

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

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

在IOS5之后,你可以这样做:

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

然后你可以做

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

在任何非主线程的线程上。

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