2

ФоновыхNSOutlineView не обновляется, когда объекты добавлены в контекст управляемого объекта из NSOperations

  • какао приложения с использованием основных данных Двух процессов - демон и основного пользовательский интерфейс
  • Daemon постоянно писать в хранилище данных
  • Процесс пользовательского интерфейса читается с одних и тех же данных
  • Столбцы в NSOutlineView в UI, привязанные к NSTreeController
  • NSTreeControllers managedObjectContext связан Применение с ключом путь delegate.interpretedMOC
  • NSTreeControllers объект устанавливается в TrainingGroup (NSManagedObject подкласс называется JGTrainingGroup)

То, что я хочу

When пользовательский интерфейс активируется, представление схемы должно обновляться с последними данными, вставленными демоном.

Проблема

Основной подход Thread

Принести все сущности Я заинтересован в, а затем перебрать их, делая refreshObject: mergeChanges: YES. Это работает нормально - элементы обновляются правильно. Тем не менее, все это работает на основном потоке, поэтому пользовательский интерфейс блокируется на 10-20 секунд, пока он обновляется. Отлично, поэтому давайте переместим эти обновления в NSOperations, которые работают в фоновом режиме. не

NSOperation Multithreaded подход

Как только я перенесу refreshObject: mergeChanges: позвонить в NSOperation, что обновление больше не работает. Когда я добавляю сообщения о регистрации, становится ясно, что новые объекты загружаются подклассом NSOperation и обновляются. Кажется, что независимо от того, что я делаю, NSOutlineView не будет обновляться.

То, что я пытался

Я возился с этим в течение 2-х дней твердых и попробовал все, что я могу думать.

  • Передача идентификаторов объекта NSOperation для обновления вместо имени объекта.
  • Сброс интерпретируемого MOC в разных точках - после обновления данных и до перезагрузки схемы.
  • Я бы подклассифицировал NSOutlineView. Я отбросил свой подкласс и вернул представление в экземпляр NSOutlineView, на случай, если бы здесь были какие-то смешные события.
  • Добавлен вызов перегруппировки для NSTreeController перед перезагрузкой данных NSOutlineView.
  • Удостоверился, что я установил интервал молчания равным 0 во всех контекстах управляемого объекта, которые я использовал.

У меня такое ощущение, что эта проблема связана с кэшированием основных объектов данных в памяти. Но я полностью исчерпал все свои идеи о том, как я могу заставить это работать.

Я был бы всегда благодарен всем, кто может пролить свет на то, почему это может не работать.

Код

подход Главная тема

// In App Delegate 
-(void)applicationDidBecomeActive:(NSNotification *)notification { 
    // Delay to allow time for the daemon to save 
    [self performSelector:@selector(refreshTrainingEntriesAndGroups) withObject:nil afterDelay:3]; 
} 

-(void)refreshTrainingEntriesAndGroups { 
    NSSet *allTrainingGroups = [[[NSApp delegate] interpretedMOC] fetchAllObjectsForEntityName:kTrainingGroup]; 
    for(JGTrainingGroup *thisTrainingGroup in allTrainingGroups) 
     [interpretedMOC refreshObject:thisTrainingGroup mergeChanges:YES]; 

    NSError *saveError = nil; 
    [interpretedMOC save:&saveError]; 
    [windowController performSelectorOnMainThread:@selector(refreshTrainingView) withObject:nil waitUntilDone:YES]; 
} 

// In window controller class 
-(void)refreshTrainingView { 
    [trainingViewTreeController rearrangeObjects]; // Didn't really expect this to have any effect. And it didn't. 
    [trainingView reloadData]; 
} 

NSOperation Multithreaded подход

// In App Delegate (just the changed method) 
-(void)refreshTrainingEntriesAndGroups { 
    JGRefreshEntityOperation *trainingGroupRefresh = [[JGRefreshEntityOperation alloc] initWithEntityName:kTrainingGroup]; 
    NSOperationQueue   *refreshQueue = [[NSOperationQueue alloc] init]; 
    [refreshQueue setMaxConcurrentOperationCount:1]; 
    [refreshQueue addOperation:trainingGroupRefresh]; 

    while ([[refreshQueue operations] count] > 0) { 
     [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.05]]; 

    // At this point if we do a fetch of all training groups, it's got the new objects included. But they don't show up in the outline view. 
    [windowController performSelectorOnMainThread:@selector(refreshTrainingView) withObject:nil waitUntilDone:YES]; 
} 

// JGRefreshEntityOperation.m 
@implementation JGRefreshEntityOperation 

@synthesize started; 
@synthesize executing; 
@synthesize paused; 
@synthesize finished; 

-(void)main { 
    [self startOperation]; 

    NSSet *allEntities = [imoc fetchAllObjectsForEntityName:entityName]; 
    for(id thisEntity in allEntities) 
     [imoc refreshObject:thisEntity mergeChanges:YES]; 

    [self finishOperation]; 
} 

-(void)startOperation { 
    [self willChangeValueForKey:@"isExecuting"]; 
    [self willChangeValueForKey:@"isStarted"]; 
    [self setStarted:YES]; 
    [self setExecuting:YES]; 
    [self didChangeValueForKey:@"isExecuting"]; 
    [self didChangeValueForKey:@"isStarted"]; 

    imoc = [[NSManagedObjectContext alloc] init]; 
    [imoc setStalenessInterval:0]; 
    [imoc setUndoManager:nil]; 
    [imoc setPersistentStoreCoordinator:[[NSApp delegate] interpretedPSC]]; 

    [[NSNotificationCenter defaultCenter] addObserver:self 
              selector:@selector(mergeChanges:) 
               name:NSManagedObjectContextDidSaveNotification 
               object:imoc]; 
} 

-(void)finishOperation { 
    saveError = nil; 

    [imoc save:&saveError]; 
    if (saveError) { 
     NSLog(@"Error saving. %@", saveError); 
    } 

    imoc = nil; 

    [self willChangeValueForKey:@"isExecuting"]; 
    [self willChangeValueForKey:@"isFinished"]; 
    [self setExecuting:NO]; 
    [self setFinished:YES]; 
    [self didChangeValueForKey:@"isExecuting"]; 
    [self didChangeValueForKey:@"isFinished"]; 
} 

-(void)mergeChanges:(NSNotification *)notification { 
    NSManagedObjectContext *mainContext = [[NSApp delegate] interpretedMOC]; 
    [mainContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:) 
            withObject:notification 
           waitUntilDone:YES]; 

} 

-(id)initWithEntityName:(NSString *)entityName_ { 
    [super init]; 
    [self setStarted:false]; 
    [self setExecuting:false]; 
    [self setPaused:false]; 
    [self setFinished:false]; 
    [NSThread setThreadPriority:0.0]; 
    entityName = entityName_; 
    return self; 
} 

@end 

// JGRefreshEntityOperation.h 
@interface JGRefreshEntityOperation : NSOperation { 
    NSString *entityName; 
    NSManagedObjectContext *imoc; 
    NSError *saveError; 
    BOOL started; 
    BOOL executing; 
    BOOL paused; 
    BOOL finished; 
} 

@property(readwrite, getter=isStarted) BOOL started; 
@property(readwrite, getter=isPaused) BOOL paused; 
@property(readwrite, getter=isExecuting) BOOL executing; 
@property(readwrite, getter=isFinished) BOOL finished; 

-(void)startOperation; 

-(void)finishOperation; 

-(id)initWithEntityName:(NSString *)entityName_; 

-(void)mergeChanges:(NSNotification *)notification; 

@end 

UPDATE 1

Я только что нашел этот вопрос. Я не могу понять, как я пропустил это, прежде чем я разместил свой, но сводка: Основные данные не были предназначены для того, чтобы делать то, что я делаю. Только один процесс должен использовать хранилище данных.

NSManagedObjectContext and NSArrayController reset/refresh problem

Однако в другой области моего приложения у меня есть два процесса обмена хранилище данных с одним Начитавшись только доступ, и это, казалось, прекрасно работать. Плюс ни один из ответов на мой last question on this topic не упоминал, что это не поддерживалось в Core Data.

Я собираюсь перестроить мое приложение так, чтобы только один процесс записывал в хранилище данных в любой момент времени. Я все еще скептически отношусь к тому, что это решит мою проблему. Мне кажется, что это больше напоминает проблему обновления NSOutlineView - объекты создаются в контексте, это просто представление схемы не подбирает их.

ответ

0

Я закончил перестраивать мое приложение. Я только импортирую элементы из одного процесса или другого сразу. И он отлично работает. Ура!

Смежные вопросы