代码之家  ›  专栏  ›  技术社区  ›  John Gallagher

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

  •  10
  • John Gallagher  · 技术社区  · 14 年前

    背景

    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...
    
    • 用户可以将组分配给项目。所有子代都设置为该项目。

    • 我用苹果公司批准的方式来做这件事 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
    

    进行关系操作

    @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
    

    数据模型

    Data Model

    我已经做了更多的实验,即使没有合并,仍然会抛出异常。只需在修改关系后将托管对象上下文保存到另一个线程中就足够了。

    我有一个与应用程序代理共享的持久存储协调器。我尝试为线程创建一个单独的NSPersistentStoreCoordinator,该线程的URL与我的数据存储相同,但核心数据出现问题。

    我很乐意就如何成为线程的协调人提出建议。核心数据文档暗示有一种方法可以做到这一点,但我不知道如何做到。

    1 回复  |  直到 14 年前
        1
  •  10
  •   ImHuntingWabbits    14 年前

    您正在穿越CoreData中非常糟糕的流(本例中是线程)。这样看:

    1. 从主线程上的按钮(假设为按钮点击,则为IBAction)调用的startTest
    2. 在主线程上的操作中创建新的托管对象上下文。
    3. 现在,操作队列从工作线程调用操作“main”方法(在这里放置一个断点,您将看到它不在主线程上)。

    解决方案:

    推荐文章