当我修改NSOperation子类中的核心数据关系时,为什么我的应用程序崩溃了?

Joh*_*her 10 cocoa multithreading core-data nsoperation

背景

我有以下对象树:

Name                       Project       
Users                      nil           
  John                     nil            
    Documents              nil           
      Acme Project         Acme Project    <--- User selects a project
        Proposal.doc       Acme Project  
          12:32-12:33      Acme Project  
          13:11-13:33      Acme Project  
            ...thousands more entries here...
Run Code Online (Sandbox Code Playgroud)
  • 用户可以将组分配给项目.所有后代都设置为该项目.

  • 这会锁定主线程,所以我正在使用NSOperations.

  • 我正在使用Apple批准的方式来执行此操作,观察NSManagedObjectContextDidSaveNotification并合并到主要环境中.

问题

我的保存失败,出现以下错误:

Failed to process pending changes before save. The context is still dirty after 100 attempts. Typically this recursive dirtying is caused by a bad validation method, -willSave, or notification handler.

我试过的

我已经剥离了我的应用程序的所有复杂性,并做了我能想到的最简单的项目.并且错误仍然存​​在.我试过了:

  • 将队列上的最大操作数设置为1或10.

  • refreshObject:mergeChanges:在NSOperation子类中的几个点调用.

  • 在托管对象上下文中设置合并策略.

  • 建立和分析.它空洞了.

我的问题

如何在没有我的应用程序崩溃的情况下在NSOperation中设置关系?当然这不能限制核心数据?它可以?

代码

下载我的项目:http://synapticmishap.co.uk/CDMTTest1.zip

主控制器

@implementation JGMainController

-(IBAction)startTest:(id)sender {
    NSManagedObjectContext *imoc = [[NSApp delegate] managedObjectContext];

    JGProject *newProject = [JGProject insertInManagedObjectContext:imoc];
    [newProject setProjectName:@"Project"];
    [imoc save];

        // Make an Operation Queue
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue setMaxConcurrentOperationCount:1]; // Also crashes with a higher number here (unsurprisingly)

    NSSet *allTrainingGroupsSet = [imoc fetchAllObjectsForEntityName:@"TrainingGroup"];

    for(JGTrainingGroup *thisTrainingGroup in allTrainingGroupsSet) {
        JGMakeRelationship *makeRelationshipOperation = [[JGMakeRelationship alloc] trainGroup:[thisTrainingGroup objectID] withProject:[newProject objectID]];
        [queue addOperation:makeRelationshipOperation];
        makeRelationshipOperation = nil;
    }
}

    // Called on app launch.
-(void)setupLotsOfTestData {
         // Sets up 10000 groups and one project
}

@end
Run Code Online (Sandbox Code Playgroud)

建立关系运作

@implementation JGMakeRelationshipOperation

-(id)trainGroup:(NSManagedObjectID *)groupObjectID_ withProject:(NSManagedObjectID *)projectObjectID_ {
    appDelegate = [NSApp delegate];
    imoc = [[NSManagedObjectContext alloc] init];
    [imoc setPersistentStoreCoordinator:[appDelegate persistentStoreCoordinator]];
    [imoc setUndoManager:nil];
    [imoc setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(mergeChanges:) 
                                                 name:NSManagedObjectContextDidSaveNotification 
                                               object:imoc];
    groupObjectID = groupObjectID_;
    projectObjectID = projectObjectID_;
    return self;
}

-(void)main {
    JGProject       *project        = (JGProject *)[imoc objectWithID:projectObjectID];
    JGTrainingGroup *trainingGroup = (JGTrainingGroup *)[imoc objectWithID:groupObjectID];
    [project addGroupsAssignedObject:trainingGroup];
    [imoc save];

    trainingGroupObjectIDs = nil;
    projectObjectID = nil;
    project = nil;
    trainingGroup = nil;
}

-(void)mergeChanges:(NSNotification *)notification {
    NSManagedObjectContext *mainContext = [appDelegate managedObjectContext];
    [mainContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)
                                  withObject:notification
                               waitUntilDone:YES];  
}

-(void)finalize {
    appDelegate = nil;
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    imoc = nil;
    [super finalize];
}
@end


@implementation NSManagedObjectContext (JGUtilities)

-(BOOL)save {
         // If there's an save error, I throw an exception
}

@end
Run Code Online (Sandbox Code Playgroud)

数据模型

数据模型

更新1

我已经尝试了一些,即使没有合并,仍然会抛出异常.只需在修改关系后将托管对象上下文保存在另一个线程中就足够了.

我有一个与app delegate共享的持久性商店协调员.我已经尝试为与我的数据存储具有相同URL的线程创建单独的NSPersistentStoreCoordinator,但Core Data抱怨.

我很乐意就如何为线程制作协调员提出建议.核心数据文档提到有一种方法可以做到,但我看不出如何做.

ImH*_*its 10

你正在穿越流(这种情况下的线程),这在CoreData中是非常糟糕的.这样看:

  1. startTest从主线程上的按钮(是IBAction,假设按钮点击)调用
  2. 你的for循环使用初始化程序trainGroup创建一个JGMakeRelationship对象:withProject :(这应该叫做init,可能会调用super,但这不会导致这个问题).
  3. 您可以在主线程上的操作中创建新的托管对象上下文.
  4. 现在,操作队列从一个工作线程调用操作"main"方法(在这里放置一个断点,你会看到它不在主线程上).
  5. 您的应用程序出现繁荣,因为您从与其创建的线程不同的线程访问了托管对象上下文.

解:

在操作的main方法中初始化托管对象上下文.

  • 这是固定的.我无法克服我是怎么看不到的.我几天来一直在强调这一点.我非常感谢你回答这个问题.请在twitter(synapticmishap)上打我,我会给你一份我的应用程序/其他一些奖励. (2认同)