2010-06-06 42 views
2

背景NSOutlineView當對象添加到被管理對象的上下文從NSOperations

  • 使用核心數據可可應用程序的兩個 過程不刷新 - 守護程序和主UI
  • 守護進程不斷寫入到數據存儲
  • UI進程從相同的數據中讀取 存儲
  • UI中的NSOutlineView中的列綁定到 NSTreeController
  • NSTreeControllers managedObjectContext勢必 與 delegate.interpretedMOC
  • NSTreeControllers實體設置爲TrainingGroup(NSManagedObject子類被稱爲JGTrainingGroup)

我想

當什麼的關鍵路徑應用UI已激活,大綱視圖應使用守護程序插入的最新數據更新。

的問題

主線程方法

我獲取所有我感興趣的實體,然後遍歷它們,這樣做refreshObject:mergeChanges:YES。這工作正常 - 項目刷新正確。但是,這些都在主線程上運行,因此UI在刷新時會鎖定10-20秒。好吧,讓我們將這些刷新移動到在後臺運行的NSOperations。

的NSOperation多線程技術

只要我移動refreshObject:mergeChanges:打電話到的NSOperation,刷新不再工作。當我添加日誌消息時,顯然新對象由NSOperation子類加載並刷新。看來不管我做什麼,NSOutlineView都不會刷新。

我已經試過

我這個好惹的2天實和嘗試一切我能想到的。

  • 將objectID傳遞給NSOperation來刷新而不是實體名稱。
  • 在數據刷新之後和大綱視圖重新加載之前的各個點重置解釋的MOOC。
  • 我想分類NSOutlineView。我放棄了我的子類並將視圖重新設置爲NSOutlineView的一個實例,以防萬一在這裏出現任何有趣的事情。
  • 在重新加載NSOutlineView數據之前,向NSTreeController添加了rearrangeObjects調用。
  • 確定我已將靜態時間間隔設置爲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多線程技術

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

更新1

我剛剛發現了這個問題。我無法理解我在發佈我之前是如何錯過它的,但總結如下:核心數據並非設計用於做我在做的事。只有一個進程應該使用數據存儲。

NSManagedObjectContext and NSArrayController reset/refresh problem

然而,在我的應用程序的不同區域,我有兩個過程用一個共享數據存儲具有隻讀訪問,這似乎正常工作。另外,我的last question on this topic的答案都沒有提到,這在覈心數據中不受支持。

我將重新構建我的應用程序,以便任何時候只有一個進程寫入數據存儲區。我仍然懷疑這會解決我的問題。它在我看來更像是一個NSOutlineView刷新問題 - 對象是在上下文中創建的,只是大綱視圖不會提取它們。

回答

0

我最終重新構建了我的應用程序。我只從一個進程或另一個進程中導入項目。它完美的作品。歡呼!

相關問題