如何在不阻塞界面的情况下加载NSTableView的数据?

时间:2010-11-22 04:35:02

标签: objective-c cocoa nstableview nsarraycontroller

我正在初始化一个简单的界面,NSTableView绑定到一个数组控制器(管理一个字典数组)。我想在后台加载数组的内容(这是一个非常耗时的过程),每100或1000个元素更新表视图。我们的想法是界面可用且响应迅速。我无法弄清楚如何在之后触发更新/刷新。桌子仍然是空的。任何人都可以提供指针吗?

我目前的做法是:

// In init for my app controller. This seems to work well, but I've tried other methods here.
[self performSelectorInBackground:@selector(loadTable) withObject:nil];


- (void)loadTable {
  tracks = [[NSMutableArray alloc] initWithCapacity:[masters count]];

  // ... create each object one-by-one. Add it to tracks.
  for (... in ...) {
    [tracks addObject:newObject];
  }

  // Now I don't know what to do next. The table remains empty. 
  // Things I've tried (though possibly not in all combinations with the
  // method above):
  // 1. With a suitably-defined reloadData method, which just reloads
  //   the table view and sets needs display.
  [self performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:YES];

  // 2. Reload directly.
  [tv reloadData];
  [tv setNeedsDisplay];
}

如果我只是直接加载数据,并且不尝试在后台执行此操作,一切正常,但需要将近30秒。

2 个答案:

答案 0 :(得分:6)

你有一个表列(我假设你的意思)绑定到一个数组控制器,所以这是表视图从中获取数据的地方。表视图可能很好地要求更新的数组,但它要求数组控制器,它不知道任何变化。

阵列控制器不会简单地转身并要求您提供新数据;这意味着它只是为了让你更难将表视图绑定到你的数组,而事实并非如此。这是一个控制器;它的工作是拥有(复制)数组并维护其顺序和用户选择其对象的某个子集。

因此,您需要阵列控制器来查找何时向阵列添加项目。实现此目标的最佳方法是将阵列控制器的contentArray绑定到控制器的属性,并以符合KVO的方式更新该属性。

这意味着:

  1. init方法中创建可变数组。 (当然,请在dealloc中发布。)
  2. 为方便起见,实施the array accessor methods,加addTracksObject:removeTracksObject:(这是技术设置的访问方法,因此KVO会忽略它们的数组属性)。
  3. 要添加曲目,请发送一条addTracksObject:消息。您应该通过向自己发送insertObject:inTracksAtIndex:消息(索引为[self countOfTracks]来回复此消息,除非您要执行insort),并且您应该通过发送{insertObject:inTracksAtIndex:来回复tracks 1}}数组insertObject:atIndex:消息。
  4. 正如我所提到的,当addFooObject:是NSArray属性时,KVO会忽略removeFooObject:foo,考虑到那些只有NSSet属性的访问者,所以你需要在insertObject:inFooAtIndex:之上实现它们。 {1}}和removeObjectFromFooAtIndex:因为那些数组访问器,这意味着KVO会对它们作出反应。

    正如我刚才描述的那样,第3步将会非常慢,因为它会导致数组控制器重新获取你的属性和表视图,以至少每次重新获取一次数组控制器的arrangedObjects对于您添加的每一行。

    因此,您应该使用此备用步骤3维护批量添加行为:

    • 实施insertTracks:atIndexes:,并向其传递一批(例如100或1000)轨道和[NSIndexSet indexSetWithRange:(NSRange){ [self countOfTracks], countOfBatch }]形成的索引集的数组。您还需要实现removeTracksAtIndexes:,因为如果您没有其对应方式,KVO将忽略每个插入方法。

    您可能应该设置阵列控制器以尝试保留选择,以免在您仍然引入行时过多地挫败用户。

    此外,您可能希望在后台线程上创建对象,定期向自己发送另一批使用主线程执行添加的批处理。我通常会主动在主线程运行循环中做事情,但是当你的定期负载建立另一批时,这种事情很容易让你的界面变得迟钝。

答案 1 :(得分:1)

您需要在主线程的表视图上调用setNeedsDisplay:YES。不要从后台线程调用它。所有Cocoa UI调用必须在主线程上完成或发生奇怪的事情。