Выполнять выборку без замораживания пользовательского интерфейса

Я делаю небольшое приложение для чата. В моем приложении я использую NSFetchedResultsController. Есть 2 стола, 1 для лобби и 1 для чата. Проблема в том, что всякий раз, когда я вхожу в чат, мне приходится ждать, пока NSFetchedResultsController выполнит выборку и загрузит все данные, прежде чем я начну вводить что-либо. Мне было интересно, можно ли предварительно выполнить выборку в фоновом режиме или как-то позволить пользователю начать вводить текст до того, как будут загружены последние сообщения.

Итак, если я установлю эту часть в методе viewDidLoad, мой пользовательский интерфейс просто зависает, пока не будут загружены все данные:

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

и если я установил этот фрагмент кода в отдельный метод, а затем вызову этот метод в viewDidLoad в фоновом режиме

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

tableView просто не обновляется, поэтому мне нужно вернуться к первому tableView, а затем вернуться, чтобы получить какие-либо результаты.

Спасибо.

Любая помощь будет оценена

Обновление

Я попробовал несколько других способов сделать это.

Способ 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
}

result: tableView не показывает никаких данных, необходимо повторно открыть этот контроллер представления

Способ 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
}

результат: обновление таблицыView, но пользовательский интерфейс зависает

Способ 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
}

результат: пользовательский интерфейс зависает, обновляет таблицу.

Как я где-то читал, все, что нужно сделать с помощью пользовательского интерфейса, должно выполняться в основном потоке. Но я все еще не могу понять, как это сделать, не замораживая tableView.

Спасибо, что прочитали.

Любая помощь приветствуется

Ответ 1

Я не уверен, будет ли это работать или нет, но вы можете попробовать:

- (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
}

Ответ 2

Как только выбор выполняется в fetchIt, вызовите метод reloadData в UITableView. Для получения дополнительной информации см. Документацию Apple на UITableView

Edit:

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

Для получения дополнительной информации посетите документацию Apple

Ответ 3

Отправляйте запрос на выборку на новый поток:

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

    dispatch_async(YourThreadName, ^{

Your Fetch Request

    });

    dispatch_release(YourThreadName);

Ответ 4

Задайте запрос выборки fetchBatchSize, чтобы вы фактически не извлекали все данные одновременно. Для заполнения экрана вам, вероятно, потребуется достаточно записей, плюс еще несколько, чтобы сохранить первоначальную прокрутку. Если эти данные являются, скажем, чат-сообщениями, я думаю, что ограничение получения последних 50 или 100 сообщений должно значительно ускорить процесс.

Ответ 5

Прежде всего, правильно индексируйте свойства объекта, чтобы запросы выполнялись быстрее, особенно при сортировке. Затем посмотрите на оптимизацию вашего запроса на выборку, установив размер партии, ограничивая данные до минимума. Используйте отладчик SQL для проверки запроса и даже проверяйте запросы снова на файл sqlite самостоятельно, используя приложение типа Base. Если вы попытаетесь использовать многопоточность без необходимости для такого простого приложения, это только ухудшит ситуацию.

FYI в вашем коде, viewDidLoad уже включен в основной поток. Если вы пытаетесь отложить выборку до загрузки изображения, вы можете использовать функцию performSelector afterDelay: 0.