如何将 ManagedObjectContext 与线程一起使用

Posted

技术标签:

【中文标题】如何将 ManagedObjectContext 与线程一起使用【英文标题】:How to use ManagedObjectContext with threads 【发布时间】:2010-12-24 16:28:53 【问题描述】:

这可能是一个非常简单的应用程序,但我是 Objective-C 的新手(来自 Java),整个内存管理和“EXC_BAD_ACCESS”错误让我心碎。

我有一个普通的 NavigationController iPhone 应用程序,带有核心数据。在 AppDelegate 中创建 NSManagedObjectContext 并将其传递给 RootViewController。直接从主线程中查找视图以填充表格,这似乎工作正常。

该应用程序是某种 RSS 类型的阅读器,因此一旦应用程序启动,我就会触发一个线程来获取新数据并更新视图:

-(void)updateData:(id)sender 
 UIActivityIndicatorView *activityIndicator =
    [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
 [activityIndicator startAnimating];
 UIBarButtonItem *activityItem =
    [[UIBarButtonItem alloc] initWithCustomView:activityIndicator];
 [activityIndicator release];
 self.navigationItem.leftBarButtonItem = activityItem;
 [activityItem release];

 // Start thread to update the data
 [NSThread detachNewThreadSelector:@selector(doUpdateData) toTarget:self withObject:nil];


-(void)doUpdateData
 NSLog(@"Update data Thread (in 5 sec.)");
 [NSThread sleepForTimeInterval:5];

 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

 DataManager *data = [[DataManager alloc] initWithContext:managedObjectContext];
 [data updateData];
 [data release];
 data=nil;

 [self performSelectorOnMainThread:@selector(finishUpdateData) withObject:nil waitUntilDone:NO]; 
 [pool release];


-(void)finishUpdateData
 self.navigationItem.leftBarButtonItem = updateBttn;
 DataManager *data = [[DataManager alloc] initWithContext:managedObjectContext];
 objects = [data getArticles];
 [data release];
 data=nil;
 NSLog(@"Amount of records after update: %d", [objects count]);
 [self.tableView reloadData]; 

问题是这行不通。在 DataManager 中,首先需要检索设置,一旦创建了 NSEntityDescription,我就会得到“EXC_BAD_ACCESS”:

- (NSFetchedResultsController *)fetchedResultsController 
    // Set up the fetched results controller if needed.
    if (fetchedResultsController == nil) 
        NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
        NSEntityDescription *entity = [NSEntityDescription entityForName:@"Setting" inManagedObjectContext:managedObjectContext];
        [fetchRequest setEntity:entity];

        NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"key" ascending:YES];
        NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];

        [fetchRequest setSortDescriptors:sortDescriptors];
  [fetchRequest setFetchLimit:1]; 
        .
        NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:nil];

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

        [aFetchedResultsController release];
        [fetchRequest release];
        [sortDescriptor release];
        [sortDescriptors release];
    

 return fetchedResultsController;
 

我猜指向 ManagedObjectContext 的指针是错误的,这是由于在不同的线程和内存池中运行所致。那么如果这是问题,你如何创建这样的应用程序),我如何获得对他线程的原始 ManagedObjectContext 格式的引用?

[编辑] 我也尝试过使用

iDomsAppDelegate *appDelegate = (iDomsAppDelegate *)[[UIApplication sharedApplication] delegate];
DataManager *data = [[DataManager alloc] initWithContext:appDelegate.managedObjectContext];

在 doUpdateData 中(正如其他帖子所暗示的那样),但结果相同

【问题讨论】:

【参考方案1】:

托管对象上下文不是线程安全的。 Apple's guidelines 表示您必须每个线程有一个单独的 NSManagedObjectContext 实例。

【讨论】:

添加了指向 Apple 指南的链接...除此之外,简短、切中要害且完全正确。

以上是关于如何将 ManagedObjectContext 与线程一起使用的主要内容,如果未能解决你的问题,请参考以下文章

将 UITabBarController 与内部 UINavigationControllers 一起使用时如何共享 ManagedObjectContext

如果它是子视图控制器,如何将 AppDelegate 中的 managedObjectContext 传递给正确的视图控制器?

如何使用 managedObjectContext 来使用实体?

将 ManagedObjectContext 传递/注入到视图控制器中

将 managedObjectContext 发送到 viewController 崩溃

如何观察 NSManagedObject 是不是从 managedObjectContext 中移除