Frage Führen Sie den Abruf durch, ohne die Benutzeroberfläche einzufrieren


Ich mache eine kleine Chat-App. In meiner App verwende ich NSFetchedResultsController. Es gibt 2 TableViews, 1 für die Lobby und 1 für den Chatroom. Das Problem ist, dass ich jedes Mal, wenn ich einen Chatroom betrete, darauf warten muss, dass NSFetchedResultsController alle Daten abholt und lädt, bevor ich etwas eingeben kann. Ich habe mich gefragt, ob es möglich ist, den Abruf im Hintergrund durchzuführen oder den Benutzer vor dem Laden der letzten Nachrichten irgendwie zu tippen zu lassen.

Wenn ich also diesen Teil in der Methode viewDidLoad einstelle, friert meine UI einfach ein, bis alle Daten geladen sind:

NSError *_error;
if (![self.fetchedResultsController performFetch:&_error]) {
    NSLog(@"Unresolved error %@, %@", _error, [_error userInfo]);
}

und wenn ich diesen Teil des Codes in eine separate Methode setze und dann diese Methode in viewDidLoad im Hintergrund aufrufen

[self performSelectorInBackground:@selector(fetchIt) withObject:nil];

TableView wird gerade nicht aktualisiert, also muss ich zurück zum ersten TableView gehen und dann zurückkommen, um irgendwelche Ergebnisse zu erhalten.

Vielen Dank.

Jede Hilfe wäre willkommen

aktualisieren

Ich habe ein paar andere Möglichkeiten versucht.

Weg 1:

-(void)reloadTheTable
{
  [self.tableView reloadData];
}

-(void)fetchIt
{
  NSError *_error;
  if (![self.fetchedResultsController performFetch:&_error]) {
    NSLog(@"Unresolved error %@, %@", _error, [_error userInfo]);
  }
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
  [self performSelectorOnMainThread:@selector(reloadTheTable) withObject:nil waitUntilDone:NO];
  [pool release];
}

- (void)viewDidLoad {
  //some code
  [self performSelectorInBackground:@selector(fetchIt) withObject:nil];
  //some other code
}

Ergebnis: tableView zeigt keine Daten an, muss diesen View-Controller erneut öffnen

Weg 2:

- (void)viewDidLoad {
  //some code
  dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
    NSError *_error;
    if (![self.fetchedResultsController performFetch:&_error]) {
        NSLog(@"Unresolved error %@, %@", _error, [_error userInfo]);
    }
  });
  //some other code
}

Ergebnis: TableView-Aktualisierungen, aber die Benutzeroberfläche bleibt stehen

Weg 3:

-(void)fetchIt
{
  NSError *_error;
  if (![self.fetchedResultsController performFetch:&_error]) {
    NSLog(@"Unresolved error %@, %@", _error, [_error userInfo]);
  }
}

- (void)viewDidLoad {
  //some code
  [self performSelectorOnMainThread:@selector(fetchIt) withObject:nil waitUntilDone:NO];
  //some other code
}

Ergebnis: Benutzeroberfläche friert ein, Tabellenansicht aktualisiert.

Wie ich irgendwo gelesen habe, muss alles, was etwas mit UI zu tun hat, in einem Hauptthread erledigt werden. Aber ich kann immer noch nicht herausfinden, wie es geht, ohne das TableView einzufrieren.

Danke fürs Lesen.

Jede Hilfe wird geschätzt


11
2018-01-11 12:37


Ursprung


Antworten:


Ich bin mir nicht sicher, ob das funktioniert oder nicht, aber du kannst es versuchen:

- (void)methodThatloadYourData {
  NSError *_error;
  if (![self.fetchedResultsController performFetch:&_error]) {
    NSLog(@"Unresolved error %@, %@", _error, [_error userInfo]);
  }
}

- (void)viewDidLoad {
  //some code

  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),
                 ^{
                   [self performSelector:@selector(methodThatloadYourData)];
                   dispatch_async(dispatch_get_main_queue(),
                                    ^{
                                      [self.tableView reloadData];
                                    });
                 });

  //some other code
}

13
2018-01-13 09:21



Senden Sie Ihre Abrufanforderung an einen neuen Thread:

dispatch_queue_t coreDataThread = dispatch_queue_create("com.YourApp.YourThreadName", DISPATCH_QUEUE_SERIAL);

    dispatch_async(YourThreadName, ^{

Your Fetch Request

    });

    dispatch_release(YourThreadName);

1
2018-01-11 16:48



Legen Sie die Abrufanforderung fest fetchBatchSize damit Sie nicht wirklich alle Daten auf einmal abrufen. Sie brauchen wahrscheinlich nur genug Datensätze, um den Bildschirm zu füllen, plus einige mehr, um das initiale Scrollen reaktionsfähig zu halten. Wenn es sich bei den fraglichen Daten beispielsweise um Chat-Nachrichten handelt, würde ich meinen, dass eine Begrenzung des Abrufs auf die letzten 50 oder 100 Nachrichten die Dinge erheblich beschleunigen sollte.


1
2018-01-11 18:22



Sobald der Abruf abgeschlossen ist fetchIt Methode, rufen Sie die reloadData Methode in UITableView. Weitere Informationen finden Sie in der Apple-Dokumentation unter UITableView

Bearbeiten:

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    [self.tableView reloadData];
}

Für weitere Informationen, besuchen Sie die Apple-Dokumentation


1
2018-01-11 12:51



Indexieren Sie die Entitätseigenschaften zunächst ordnungsgemäß, damit die Abfragen schneller ausgeführt werden, insbesondere beim Sortieren. Sehen Sie sich dann die Optimierung Ihrer Abrufanforderung an, indem Sie die Stapelgröße festlegen und die Daten auf das erforderliche Minimum beschränken. Verwenden Sie den SQL-Debugger, um die Abfrage zu untersuchen, und testen Sie die Abfragen selbst anhand einer App wie Base selbst anhand der SQLite-Datei. Wenn Sie versuchen, Multithreading unnötig für solch eine einfache App zu verwenden, wird es nur noch schlimmer machen.

FYI in Ihrem Code, ViewDidLoad ist bereits auf dem Hauptthread. Wenn Sie versuchen, den Abruf zu verzögern, bis die Ansicht geladen ist, können Sie performSelector afterDelay: 0 verwenden.


0
2017-09-10 11:52