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

mar*_*eed 0 cocoa objective-c 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];
}
Run Code Online (Sandbox Code Playgroud)

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

Pet*_*sey 6

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

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

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

这意味着:

  1. init方法中创建可变数组.(当然,发布它dealloc.)
  2. 执行数组访问方法,再加上addTracksObject:removeTracksObject:(在技术上set访问方法,所以国际志愿者组织会忽略他们的一个数组属性),为您提供方便.
  3. 要添加曲目,请向您自己发送addTracksObject:消息.你应该通过向自己发送一条insertObject:inTracksAtIndex:消息来响应([self countOfTracks]除了你想做一个insort之外的索引),你应该insertObject:inTracksAtIndex:通过向你的tracks阵列发送insertObject:atIndex:消息来回应.

正如我所提到的,KVO将忽略addFooObject:并且removeFooObject:何时foo是NSArray属性,考虑到那些只有NSSet属性访问器,因此您需要在它们之上实现它们insertObject:inFooAtIndex:,removeObjectFromFooAtIndex:因为它们数组访问器,这意味着KVO将对它们作出反应.

正如我刚才描述的那样,第3步将非常缓慢,因为它将导致阵列控制器重新获取您的属性和表视图,以便arrangedObjects为您添加的每一行至少重新获取一次阵列控制器.

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

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

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

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