核心数据线程和锁争用问题

Jan*_*edi 5 multithreading cocoa-touch core-data objective-c ios

我目前正在编写iOS应用的同步引擎.我正在编写的方法之一是重载数据功能,其中应用程序重新下载用户的数据以及他们的所有照片.这是一个昂贵的操作(按时间),所以我创建了一个NSOperation子类,SSReloadDataOperation.它下载数据,获取currentUser实体,从当前用户中删除所有现有照片,然后重新填充它.

但是,即使我认为这是线程安全的,有时当操作正在运行并且-currentUser从其他地方访问时,应用程序崩溃,大概是在尝试获取它时.其他时候,UI有时会冻结,并且在调试器中暂停显示它总是在-currentUser NSFetchRequest执行调用时停止.

我如何使这个操作线程安全和原子,这样我可以下载和重新填充而不会阻塞主UI线程,仍然-currentUser可以访问?在使用锁或架构方面我是否缺少某些东西?谢谢!

码:

- (void)main
{
  // Download the photo data
  [[SyncEngine defaultEngine] getUserPhotosWithCompletionBlock:^(NSMutableArray *photos)
  {
     if (photos)
     {
         // Create a new NSManagedObjectContext for this operation

         SSAppDelegate* appDelegate = [[UIApplication sharedApplication] delegate];
         NSManagedObjectContext* localContext = [[NSManagedObjectContext alloc] init];
         [localContext setPersistentStoreCoordinator:[[appDelegate managedObjectContext] persistentStoreCoordinator]];

         NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter];
         [notificationCenter addObserver:self
                                selector:@selector(mergeChanges:)
                                    name:NSManagedObjectContextDidSaveNotification
                                  object:localContext];

         NSError* error;
         NSFetchRequest* request = [[[SyncEngine defaultEngine] managedObjectModel] fetchRequestFromTemplateWithName:@"CurrentUser" substitutionVariables:[[NSDictionary alloc] init]];
         User* currentUser = [[localContext executeFetchRequest:request error:&error] objectAtIndex:0];

         // Remove the old data
         [currentUser setPhotos:[[NSSet alloc] init]];

         // Iterate through photo data, repopulate
         for (Photo* photo in photos) {
             [currentUser addPhotosObject:photo];
         }

         if (! [localContext save:&error]) {
             NSLog(@"Error saving: %@", error);
         }

         NSLog(@"Completed sync!");
     }
  } userId:[[[SyncEngine defaultEngine] currentUser] userId]];
}
Run Code Online (Sandbox Code Playgroud)

-currentUser方便方法,通常从主线程调用.

- (User *)currentUser
{
   NSError* error;
   NSFetchRequest* request = [self.managedObjectModel fetchRequestFromTemplateWithName:@"CurrentUser" substitutionVariables:[[NSDictionary alloc] init]];
    NSArray* result = [self.managedObjectContext executeFetchRequest:request error:&error];
    if ([result count] == 1) {
    return [result objectAtIndex:0];
    }
    return nil;
}
Run Code Online (Sandbox Code Playgroud)

eof*_*ter 1

你是对的,这种冻结感觉像是线程问题。

\n\n

因为托管对象必须在使用其上下文和创建它们的同一线程或串行队列中使用,所以不可能有像currentUser示例中那样的方法,并使其神奇地返回线程安全的托管对象。

\n\n

但您可以将上下文作为参数传递。调用者将决定在哪种上下文中复活用户。

\n\n
- (User *)userWithContext:(NSManagedContext *)context {\n    User *user = nil;\n\n    NSError *error;\n    NSFetchRequest *request = ...;\n    NSArray *userArray = [context executeFetchRequest:request error:&error];\n    if (userArray == nil) {\n        NSLog(@\xe2\x80\x9cError fetching user: %@\xe2\x80\x9c, error);\n    } else if ([userArray count] > 0) {\n        user = userArray[0];\n    }\n\n    return user;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

这是关于您的代码片段的其他想法。

\n\n

您在操作\xe2\x80\x99s main 中创建的上下文可以是主上下文的子上下文。然后,保存该子项后,保存父项(如果您希望此时将数据存储到存储中)。使用父子关系时,您不需要订阅 did-save 通知并合并其中的更改。

\n\n

从技术上讲,您可以从操作\xe2\x80\x99s main 访问主上下文的持久存储协调器。因为它只允许与来自一个线程或队列的上下文一起工作(调用任何方法)。我打赌你在主线程上创建了你的主要上下文。

\n