多线程 – 为什么在NSOperation子类中修改Core Data关系时,我的应用程序崩溃?

背景

我有以下对象树:

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...

>用户可以为一个项目分配一个组.所有后代都被设置为该项目.
>这会锁定主线程,所以我使用NSOperations.
>我正在使用Apple批准的这种方法,看NSManagedobjectContextDidSaveNotification并合并到主要的上下文中.

问题

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

保存前无法处理挂起的更改. 100次尝试后,上下文仍然很脏.通常这个递归的脏污是由一个坏的验证方法,-willSave或通知处理程序引起的.

我试过的

我已经把我的应用程序的所有复杂性都消除掉了,并且做了我能想到的最简单的项目.并且错误仍然发生.我试过了:

>将队列上的最大操作数设置为1或10.
>调用refreshObject:mergeChanges:在NSOperation子类的几个点.
>在受管对象上下文中设置合并策略.
>建立和分析.它变空了

我的问题

没有我的应用程序崩溃,我如何在NSOperation中设置关系?当然这不是Core Data的限制吗?它可以?

代码

下载我的项目: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

进行关系操作

@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

数据模型

更新1

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

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

我很乐意提供关于如何使线程的协调者的建议.核心数据文件提到有一种方法,但我看不到如何.

解决方法

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

startTest从一个按钮调用(是IBAction,假设按钮点击)在主线程上
>您的for循环使用初始化程序trainGroup创建一个JGMakeRelationship对象:withProject:(这应该称为init,并且可能调用super,但这不会导致此问题).
>在主线程中,在操作中创建一个新的托管对象上下文.
>现在操作队列从工作线程调用操作“main”方法(在这里放置一个断点,你会看到它不在主线程上).
>您的应用程序变得繁荣,因为您已经从与其创建的线程不同的线程访问了受管对象上下文.

解:

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

相关文章

最近看了一下学习资料,感觉进制转换其实还是挺有意思的,尤...
/*HashSet 基本操作 * --set:元素是无序的,存入和取出顺序不...
/*list 基本操作 * * List a=new List(); * 增 * a.add(inde...
/* * 内部类 * */ 1 class OutClass{ 2 //定义外部类的成员变...
集合的操作Iterator、Collection、Set和HashSet关系Iterator...
接口中常量的修饰关键字:public,static,final(常量)函数...