2010-11-22 7 views
0

Ich initialisiere eine einfache Schnittstelle mit einem NSTableView, das an einen Array-Controller gebunden ist (der ein Array von Wörterbüchern verwaltet). Ich möchte den Inhalt für das Array im Hintergrund laden (es ist ein sehr zeitaufwendiger Prozess), indem ich die Tabellenansicht alle 100 oder 1000 Elemente aktualisiere. Die Idee ist, dass die Schnittstelle verfügbar und reaktionsfähig ist. Ich kann nicht herausfinden, wie man später auch ein Update/Refresh auslöst. Der Tisch bleibt leer. Kann jemand Zeiger anbieten?Wie kann ich Daten für ein NSTableView laden, ohne die Schnittstelle zu blockieren?

Mein aktueller Ansatz ist:

// 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]; 
} 

Wenn ich die Daten geladen werden nur direkt, und versuchen Sie nicht, dass im Hintergrund zu tun, alles funktioniert gut, aber es dauert fast 30.

Antwort

6

Sie haben die Tabellenspalten (ich nehme an, Sie meinten), die an einen Array-Controller gebunden sind, wo die Tabellenansicht ihre Daten abruft. Die Tabellenansicht kann sehr gut nach aktualisierten Arrays fragen, aber es fragt den Array-Controller, der nichts weiß, dass sich etwas geändert hat.

Der Array Controller dreht sich nicht einfach um und fragt Sie nach neuen Daten; Das würde bedeuten, dass es nur existiert, um es für Sie schwieriger zu machen, die Tabellenansicht an Ihr Array zu binden, und das ist nicht der Fall. Es ist ein Controller; Seine Aufgabe besteht darin, das Array zu besitzen (eine Kopie davon) und seine Reihenfolge und die Auswahl einer Teilmenge seiner Objekte durch den Benutzer beizubehalten.

Daher benötigen Sie den Array-Controller, um herauszufinden, wenn Sie Elemente zu Ihrem Array hinzufügen. Der beste Weg, dies zu erreichen, besteht darin, die contentArray des Array-Controllers an eine Eigenschaft Ihres Controllers zu binden und diese Eigenschaft in einer KVO-kompatiblen Weise zu aktualisieren.

Das bedeutet:

  1. Erstellen Sie die änderbaren Array in Ihrem init Methode. (Und natürlich, lassen Sie es in dealloc.)
  2. the array accessor methods implementieren sowie addTracksObject: und removeTracksObject: (die technisch Satz Accessormethoden sind, so KVO sie für eine Array-Eigenschaft ignorieren) für Ihre Bequemlichkeit.
  3. Um eine Spur hinzuzufügen, senden Sie sich eine addTracksObject: Nachricht. Sie sollten darauf antworten, indem Sie sich selbst eine insertObject:inTracksAtIndex: Nachricht senden (mit [self countOfTracks] für den Index, es sei denn, Sie möchten eine Insort), und Sie sollten auf insertObject:inTracksAtIndex: antworten, indem Sie Ihr tracks Array eine insertObject:atIndex: Nachricht senden.

Wie bereits erwähnt, wird KVO addFooObject: und removeFooObject: ignorieren, wenn foo eine NSArray Eigenschaft ist, diese nur NSSet-Eigenschaftenaccessoren Berücksichtigung, so dass Sie sie oben auf insertObject:inFooAtIndex: und removeObjectFromFooAtIndex: implementieren müssen, weil diejenigen Array Accessoren sind , was bedeutet, dass KVO auf sie reagieren wird.

Schritt 3, wie ich es gerade beschrieben habe, wird ziemlich langsam sein, weil es den Array-Controller dazu bringt, die Eigenschaft und die Tabellenansicht erneut zu holen, um die Array-Controller arrangedObjects mindestens einmal für jede Zeile zu holen du fügst hinzu.

Also, sollten Sie Ihr Batch-Zugabe Verhalten mit diesem alternativen Schritt 3 halten:

  • insertTracks:atIndexes: implementieren, und übergibt ihm eine Reihe von einer Charge (zB 100 oder 1000) Spuren und ein Index-Set gebildet durch [NSIndexSet indexSetWithRange:(NSRange){ [self countOfTracks], countOfBatch }]. Sie müssen auch removeTracksAtIndexes: implementieren, nur weil KVO jede Einfügemethode ignoriert, wenn Sie nicht auch das Gegenstück haben.

Sie sollten wahrscheinlich die Array-Controller gesetzt haben, zu versuchen, um die Auswahl zu bewahren, um nicht den Benutzer zu viel zu vereiteln, während Sie in Reihen bringen nach wie vor sind.

Sie können auch die Objekte in einem Hintergrundthread erstellen und sich regelmäßig einen weiteren Batch senden, der mit einem main-thread perform hinzugefügt werden soll. Ich bin normalerweise ein Verfechter der Dinge auf der Hauptthreadlaufschleife wann immer möglich zu machen, aber diese Art von Sache könnte leicht Ihre Schnittstelle lückenhaft machen, während Ihre periodische Last einen weiteren Stapel aufbaut.

1

Sie müssen setNeedsDisplay:YES in Ihrer Tabellenansicht auf dem Hauptthread aufrufen. Nennen Sie es nicht von einem Hintergrund-Thread. Alle Cocoa UI-Aufrufe müssen im Hauptthread ausgeführt werden oder es passieren seltsame Dinge.