【问题标题】:NSOutlineView not refreshing when objects added to managed object context from NSOperations当对象从 NSOperations 添加到托管对象上下文时,NSOutlineView 不刷新
【发布时间】:2010-06-06 13:49:08
【问题描述】:

背景

  • Cocoa app 使用核心数据二 进程 - 守护进程和主 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 以进行刷新,而不是实体名称。
  • 在不同点重置解释型MOC - 在数据刷新之后和大纲视图重新加载之前。
  • 我将 NSOutlineView 子类化。我丢弃了我的子类并将视图重新设置为 NSOutlineView 的实例,以防万一这里发生任何有趣的事情。
  • 在重新加载 NSOutlineView 数据之前添加了对 NSTreeController 的重新排列对象调用。
  • 确保在我使用的所有托管对象上下文中将过时间隔设置为 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

我刚刚发现了这个问题。在发布之前我无法理解我是如何错过它的,但总结是:Core Data 并不是为了做我正在做的事情而设计的。只有一个进程应该使用数据存储。

NSManagedObjectContext and NSArrayController reset/refresh problem

但是,在我的应用程序的不同区域,我有两个进程共享一个数据存储,其中一个具有只读访问权限,这似乎工作正常。另外,我的last question on this topic 的答案都没有提到 Core Data 不支持这一点。

我将重新设计我的应用程序,以便在任何时候只有一个进程写入数据存储。我仍然怀疑这会解决我的问题。在我看来,它更像是一个 NSOutlineView 刷新问题——对象是在上下文中创建的,只是大纲视图没有拾取它们。

【问题讨论】:

    标签: cocoa core-data nsoperation nsoutlineview nstreecontroller


    【解决方案1】:

    我最终重新设计了我的应用程序。我一次只从一个进程或另一个进程中导入项目。而且效果很好。万岁!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-26
      相关资源
      最近更新 更多