Как уведомить UITableViewController, что данные готовы для отображения? - PullRequest
5 голосов
/ 24 июня 2011

У меня есть UITableViewController, о котором я хотел бы уведомить, когда данные соответствующей модели будут готовы для отображения. Проблема заключается в том, что эти данные извлекаются из веб-службы, и запрос может занять до нескольких секунд. В настоящее время я синхронно извлекаю данные из основного потока, что, естественно, приводит к блокировке основного потока. Теперь я не хочу, чтобы мой контроллер знал что-либо о загрузке данных из Интернета. Как я могу сделать это. В настоящее время я думаю об использовании GCD и реализации такого метода, как -loadDataWithCallback:, и о предоставлении обратного вызова, который запускает [tableView reloadData] в случае успеха. Это хороший подход? Есть ли другие возможности уведомить контроллер о том, что модель готова? У меня была другая идея - использовать механизм делегирования и установить контроллер в качестве делегата моей модели?

Подводя итог, что лучше: GCD с обратными вызовами или реализация собственного механизма делегатов?

Есть ли другие возможности?


Обновление : 24 июня 2011 г. 13:15 CET

После прочтения всех ваших ответов я прихожу к выводу, что есть 3 возможных решения моей проблемы:

  1. Используйте NSNotifications и используйте NSURLConnection для реализации асинхронного. скачать

  2. Реализация пользовательского протокола и использование механизма делегирования. Опять же, используйте NSURLConnection для реализации асинхронного. скачать.

  3. Использовать синхронную загрузку в отдельной очереди GCD и использовать обратные вызовы.

Поскольку никто не одобряет последнее решение, я хочу обсудить этот подход немного глубже. После просмотра всего кода, который задействован в обработке уведомлений, я думаю, что GCD - лучший подход. Вместо согласования определенного уведомления, которое должно быть каким-то образом задокументировано, чтобы каждый разработчик знал об этом, я могу просто использовать обратный вызов. С одной стороны, он дает мне понятный интерфейс, подобный тому, который был бы у меня при использовании делегата, с другой стороны, он дает мне полную гибкость. Вы действительно думаете, что GCD слишком сложен для этого? Вот мой код:

- (void)loadRestaurantsWithCallback:(void (^)())callback
{
    dispatch_queue_t current_queue = dispatch_get_current_queue();
    dispatch_queue_t download_queue = dispatch_queue_create("Download queue", NULL);

    dispatch_async(download_queue, ^{
        self.restaurants = [self loadRestaurants];
        dispatch_async(current_queue, ^{ callback(); });
    });

    dispatch_release(download_queue);
}

Кстати, мое приложение просто отображает меню разных столовых в моем университете.

В моем контроллере я просто делаю следующее:

if (![self.canteen hasRestaurants]) {
    [self.canteen loadRestaurantsWithCallback:^{
        [self.tableView reloadData];
    }];
}

Это работает как шарм. Что вы думаете об этом решении?


Обновление : 24 июня 2011 г. 16:30 CET

Существует четвертое решение этой проблемы, и, вероятно, это правильный путь, даже если он включает в себя больше кода, чем подход GCD. Вот что я придумал:

  1. Используйте NSURLConnection для асинхронной загрузки.

  2. Пусть ваша модель ответит на обратные вызовы, отправленные экземпляром NSURLConnection.

  3. Использование Кодирование значения ключа и Наблюдение значения ключа .

В вашем контроллере вы просто должны сделать следующее:

[self.model addObserver:self forKeyPath:@"method-name" options:0 context:NULL];

и

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    [self.tableView reloadData];
}

Ответы [ 5 ]

4 голосов
/ 24 июня 2011

Лучший способ использовать NSNotificationCenter, создать локальный тип уведомления и опубликовать его, как только вы получите данные.

Первый регистр для типа уведомления DataUpdateNotification .

- (void)viewWillAppear:(BOOL)animated 
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(receiveDataNotification:) 
                                                 name:@"DataUpdateNotification"
                                               object:nil];
   ...............

}

Реализация receiveDataNotification: для обработки DataUpdateNotification уведомлений типа.

- (void) receiveDataNotification:(NSNotification *) notification
{
     if ([[notification name] isEqualToString:@"DataUpdateNotification"])
     {
        NSLog (@"Successfully received the Data Update notification!");
     }
}

Удалите уведомление из экземпляра вашего объекта, когда ваш контроллер исчезнет.

- (void)viewWillDisappear:(BOOL)animated 
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];

}

Теперь опубликуйте уведомление из любой части вашего приложения.

- (void) DataUpdated
{
    [[NSNotificationCenter defaultCenter] postNotificationName:@"DataUpdateNotification" object:self];
}
1 голос
/ 24 июня 2011

если вы используете потоки .. вы можете использовать

[self performSelectorOnMainThread: @selector ( // ) waitUntilDone:YES]

Это будет гарантировать, что ваш процесс ожидает, пока другой процесс не завершится.

1 голос
/ 24 июня 2011

Как насчет реализации делегата для вашего класса обработки и использования метода делегата для информирования вызывающей стороны.

Это довольно просто и легко удерживается и отлично работает в таких асинхронных сценариях.

Определение протокола делегата и создание объекта для класса. Реализуйте методы делегата в классе вызывающего объекта и, когда действие завершено, вызовите делегат.

Я был бы рад написать здесь немного кода, если вам нужно.

1 голос
/ 24 июня 2011

Мое предложение - использовать асинхронную связь вместо многопоточности (GCD). Намного легче иметь дело, и он делает то, что вам нужно.

Идея такова:

  1. вы выполняете асинхронный веб-запрос, поэтому не блокируете;

  2. при асинхронной выдаче запроса вы регистрируете «обратный вызов» с ним;

  3. когда данные есть, базовая коммуникационная инфраструктура вызывает «обратный вызов», чтобы вы знали, что данные есть;

  4. обратный вызов обычно является делегатом некоторых ваших классов (даже ваш контроллер это сделает), и его основной обязанностью является обновление вашей модели и повторная загрузка представления таблицы.

Это очень простая и чистая парадигма в использовании.

Таким образом, вы можете исследовать возможности асинхронности, предлагаемые NSURLConnection/NSURLRequest, или (настоятельно рекомендуется) взглянуть на ASIHTTRequest , который сделает ваш выбор намного проще.

0 голосов
/ 27 сентября 2011

У меня похожая ситуация, и я использую NSFetchedResultsController для ее решения.Таблица обновляется с помощью контроллера полученных результатов, и клиент работает асинхронно.Когда данные получены, я создаю модель и записываю ее с использованием CoreData, после чего контроллер автоматически выбирает ее для вставки, обновления или удаления в таблицу.

...