2

我正在考虑如何将一个非常大的核心数据树结构的绘图卸载到 CATiledLayer。CATiledLayer 似乎很棒,因为它在后台线程上执行绘图,然后在绘制时淡入平铺。但是,由于绘图信息来自设计上不是线程安全的 Core Data 上下文,因此我遇到了绘图代码需要访问 CD 上下文的竞争条件问题。

通常,如果我需要使用 Core Data 执行后台任务,我会在后台线程中创建一个新上下文并重用现有模型和持久存储协调器,以防止线程问题。但是 CATiledLayer 在内部完成所有线程,所以我不知道何时创建上下文,并且需要某种上下文共享,否则我无法将正确的实体传递给 CATiledLayer。

有没有人建议我如何处理这种情况?

干杯,埃里克-保罗。

4

2 回答 2

1

最简单的解决方案是使用调度 API 将所有数据访问锁定在单个线程上,同时仍然允许实际绘图是多线程的。

如果您现有的托管对象上下文只能在主线程上访问,那么您可以这样做:

- (void)drawInContext:(CGContextRef)context // I'm using a CATiledLayer subclass. You might be using a layer delegate instead
{
  // fetch data from main thread
  __block NSString *foo;
  __block NSString *bar;
  dispatch_sync(dispatch_get_main_queue(), ^{
    NSManagedObject *record = self.managedObjecToDraw;
    foo = record.foo;
    bar = record.bar;
  });

  // do drawing here
}

这是一个快速简单的解决方案,但它会在获取数据时锁定您的主线程,这几乎肯定会在滚动时加载新图块时产生“故障”。为了解决这个问题,您需要在“串行”调度队列上执行所有数据访问。

队列需要拥有自己的托管对象上下文,并且您需要使该上下文与主线程上的上下文保持同步,主线程(可能)由用户操作更新。最简单的方法是观察上下文已更改的通知,并丢弃用于绘图的通知。

为队列定义一个实例变量:

@interface MyClass
{
  NSManagedObjectContext *layerDataAccessContext;
  dispatch_queue_t layerDataAccessQueue;
}
@end

在您的 init 方法中创建它:

- (id)init
{
  layerDataAccessQueue = dispatch_queue_create("layer data access queue", DISPATCH_QUEUE_SERIAL);

  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contextDidChange:) name:NSManagedObjectContextDidSaveNotification object:nil]; // you might want to create your own notification here, which is only sent when data that's actually being drawn has changed
}

- (void)contextDidChange:(NSNotification *)notif
{
  dispatch_sync(layerDataAccessQueue, ^{
    [layerDataAccessContext release];
    layerDataAccessContext = nil;
  });
  [self.layer setNeedsDisplay];
}

并在绘图时访问上下文:

- (void)drawInContext:(CGContextRef)context
{
  // fetch data from main thread
  __block NSString *foo;
  __block NSString *bar;
  dispatch_sync(layerDataAccessQueue, ^{
    NSManagedObject record = self.managedObjectToDraw;
    foo = record.foo;
    bar = record.bar;
  });

  // do drawing here
}

- (NSManagedObject *)managedObjectToDraw
{
  if (!layerDataAccessContext) {
    __block NSPersistentStoreCoordinator *coordinator;
    dispatch_sync(dispatch_get_main_queue(), ^{
      coordinator = [self persistentStoreCoordinator];
    });

    layerDataAccessContext = [[NSManagedObjectContext alloc] init];
    [layerDataAccessContext setPersistentStoreCoordinator:coordinator];
  }

  NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
  NSEntityDescription *entity =
      [NSEntityDescription entityForName:@"Employee"
              inManagedObjectContext:layerDataAccessContext];
  [request setEntity:entity];

  NSPredicate *predicate =
      [NSPredicate predicateWithFormat:@"self == %@", targetObject];
  [request setPredicate:predicate];

  NSError *error = nil;
  NSArray *array = [layerDataAccessContext executeFetchRequest:request error:&error];
  NSManagedObject *record;
  if (array == nil || array.count == 0) {
    // Deal with error.
  }

  return [array objectAtIndex:0];
}
于 2012-01-20T21:09:45.203 回答
0

我已经放弃尝试在 CATiledLayer 绘制之间共享托管对象上下文实例,现在只需在每次调用时分配/初始化一个新上下文drawLayer:inContext:性能损失并不明显,因为绘图已经是异步的。

如果有人有更好的解决方案,请分享!

于 2011-02-24T12:21:23.410 回答