【问题标题】:How can I load data for an NSTableView without blocking the interface?如何在不阻塞接口的情况下为 NSTableView 加载数据?
【发布时间】:2010-11-22 04:35:02
【问题描述】:

我正在初始化一个简单的接口,其中一个 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 秒。

【问题讨论】:

    标签: objective-c cocoa nstableview nsarraycontroller


    【解决方案1】:

    您将表格列(我假设您的意思)绑定到数组控制器,因此表格视图从那里获取数据。表格视图很可能要求更新数组,但它要求数组控制器,它不知道有任何变化。

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

    因此,您需要数组控制器来找出您何时将项目添加到数组中。实现这一点的最佳方法是将数组控制器的 contentArray 绑定到控制器的属性,并以符合 KVO 的方式更新该属性。

    这意味着:

    1. init 方法中创建可变数组。 (当然,在dealloc 中发布它。)
    2. 为了方便起见,实现 the array accessor methods,加上 addTracksObject:removeTracksObject:(从技术上讲,它们是设置访问器方法,因此 KVO 将忽略它们作为数组属性)。
    3. 要添加曲目,请给自己发送addTracksObject: 消息。你应该通过给自己发送一个insertObject:inTracksAtIndex: 消息来回应这个问题(用[self countOfTracks] 作为索引,除非你想做一个insort),你应该通过给你的tracks 数组发送一个insertObject:atIndex: 来回应insertObject:inTracksAtIndex:消息。

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

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

    因此,您应该使用此备用步骤 3 来保持批量添加行为:

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

    您可能应该将数组控制器设置为尝试保留选择,以免在您仍在引入行时让用户感到沮丧。

    此外,您可能希望在后台线程上创建对象,定期向自己发送另一批以使用主线程执行添加。我通常提倡尽可能在主线程运行循环上做事,但这种事情很容易让你的界面变得迟钝,而你的周期性负载又会增加一批。

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-05-02
        • 1970-01-01
        相关资源
        最近更新 更多