背景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刷新問題 - 對象是在上下文中創建的,只是大綱視圖不會提取它們。