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

核心数据:合并添加在背景上下文中的子级是很有趣的

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

    背景

    • 多线程核心数据应用

    • NSTreeController NSOutlineView 带绑定

    • 在后台上下文上的NSOperation中创建子对象

    • 合并到主上下文中,使用 mergeChangesFromContextDidSaveNotification

    Data Model

    问题

    • 如果我将20个子创建操作排队,则合并完成后,在大纲视图中只能看到大约10-15个子对象。

    • 如果我将max concurrent operations设置为1,那么它可以完美地工作,并且可以看到20个子项。

    我要做的是不可能的吗?我可以看到核心数据可能难以成功完成合并。或者我的代码有问题吗?

    JGGroupController公司

     -(id)init {
         self = [super init];
         queue = [[NSOperationQueue alloc] init];
         [queue setMaxConcurrentOperationCount:10]; // If this is 1, it works like a dream. Anything higher and it bombs.
         return self;
     }
    
     -(IBAction)addTrainingEntryChild:(id)sender {
         moc  = [[NSApp delegate] managedObjectContext];
         JGTrainingBase *groupToAddTo = [[tree selectedObjects] objectAtIndex:0];
         for (NSUInteger i = 0; i < 20; i++) {
             JGAddChildrenObjectOperation    *addOperation = [[JGAddChildrenObjectOperation alloc] init]; 
             [addOperation addChildObjectToGroup:[groupToAddTo objectID]];
             [queue addOperation:addOperation];
         }
     }
    

    JGAddChildrenObjectOperation-NSOperation子类

     -(id)addChildObjectToGroup:(NSManagedObjectID *)groupToAddToID_ {
         groupToAddToObjectID = groupToAddToID_;
         return self;
     }
    
     -(void)main {
         [self startOperation];
         JGTrainingBase *groupToAddTo    = (JGTrainingBase *)[imoc objectWithID:groupToAddToObjectID];
         JGTrainingBase *entryChildToAdd = [JGTrainingBase insertInManagedObjectContext:imoc];
         [groupToAddTo addChildren:[NSSet setWithObject:entryChildToAdd]];
         [imoc save];
     [self cleanup];
         [self finishOperation];
     }
    
     -(void)mergeChanges:(NSNotification *)notification {
         NSManagedObjectContext *mainContext = [[NSApp delegate] managedObjectContext];
         [mainContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)
                                       withObject:notification
                                    waitUntilDone:YES];  
     }
    
    
     -(void)startOperation {
                // Omitted - Manage isExecuting, isPaused, isFinished etc flags
    
         imoc = [[NSManagedObjectContext alloc] init];
         [imoc setPersistentStoreCoordinator:[[NSApp delegate] persistentStoreCoordinator]];
         [imoc setUndoManager:nil];
         [imoc setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
         [imoc setStalenessInterval:0];
    
         [[NSNotificationCenter defaultCenter] addObserver:self
                                                  selector:@selector(mergeChanges:) 
                                                      name:NSManagedObjectContextDidSaveNotification 
                                                    object:imoc];
     }
    
     -(void)finishOperation {
                // Omitted - Manage isExecuting, isPaused, isFinished etc flags
     }
    
    1 回复  |  直到 14 年前
        1
  •  1
  •   ImHuntingWabbits    14 年前

    您的操作正在使用存储中实体的不同“版本”。考虑以下操作顺序:

    创建两个操作,我们称它们为O:F和O:G,它们将子F和G添加到组1中,称为G:1,子条目集为[a、B、C、D、E]。

    操作队列同时将O:F和O:G出列,因此它们都会获取托管对象上下文和实体G:1。

    O: G设置G的子项:2到[A,B,C,D,E,G]。

    我认为CoreData应该在其中一个线程中抛出乐观的锁定错误,因为它的更改将过时。但我可能错了。

    归根结底,您在线程之间对同一个对象进行变异,而不同步对象的状态。与创建20个操作不同,创建1个操作添加20个对象,但您有一个核心架构问题,即尝试在不同步的情况下从多个线程对同一对象进行变异。

    每次都会失败。