Какой шаблон обновить интерфейс, используя фоновый поток?

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

dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_async(aQueue, ^() {

//Backgroud code

    dispatch_sync(dispatch_get_main_queue(), ^{
    //Update the UI
    }
}

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

dispatch_sync(dispatch_get_main_queue(), ^{
    if (mylabel != nil)  && ([mylabel superview] != nil) {
        mylabel.text = _result_from_computation_;
    }
}

Есть несколько лучших способов?

Благодарю.

2
nl ja de
другая проблема с этим кодом находится в классическом макете «master-> detail». Когда я показываю объект в подробном представлении, я должен быть уверен, что в не получают никакого «старого» обновления.
добавлено автор IgnazioC, источник
Возможно, я могу использовать MVC оригинальным способом, моя модель может отправить уведомление контроллеру, когда заканчивается код фона.
добавлено автор IgnazioC, источник
Не могли бы вы немного рассказать о вреде, связанном с обновлением mylabel , если он отключен? Какую ситуацию вы там делаете?
добавлено автор Carl Veazey, источник
добавлено автор pbibergal, источник
Я не вижу мьютекса в качестве хорошего решения: я могу ошибаться, но похоже, что он хочет получить весь код async. О коде: безопасно вызывать селектор типа setText на объекте nil, поэтому тест (mylabel! = Nil) не требуется. Еще не решение, просто меньше кода для написания ...
добавлено автор il Malvagio Dottor Prosciutto, источник
О, я догадался, что вам нужно обновить интерфейс, когда фоновый процесс запущен, а не в конце. В любом случае отправьте уведомление или используйте какой-либо KVO, который должен работать, даже если может потребоваться более шаблонный код.
добавлено автор il Malvagio Dottor Prosciutto, источник

2 ответы

Вы очень хорошо это понимаете. Однако, если вы хотите больше читать или хотите более подробное объяснение того, что происходит ...

Вы должны прочитать Документы Apple Grand Central Dispatch ( GCD) Ссылка и посмотреть видео WWDC 2012, Сессия 712 - Асинхронные проектные колодки с блоками, GCD и XPC .

Если вы работаете с iOS, вы можете игнорировать XPC (interprocess communication), поскольку она не поддерживается текущей версией ОС (6.1 на момент написания этой статьи).

Example: Load a large image in the background and set the image when completed.

@interface MyClass ()
@property (strong) dispatch_block_t task;
@end

@implementation MyClass
- (void)viewDidLoad {
    self.task = ^{
       //Background Thread, i.e., your task
        NSImage *image = [[NSImage alloc] initWithData:data];
        dispatch_async(dispatch_get_main_queue(), ^{
           //Main Thread, setting the loaded image
            [view setImage:image];
        });
    });
}

- (IBAction)cancelTaskButtonClick:(id)sender { //This can be -viewWillDisappear
    self.task = nil; //Cancels this enqueued item in default global queue
}

- (IBAction)runTaskButtonClick:(id)sender {
   //Main Thread
    dispatch_queue_t queue;
    queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, self.task);
}

Чтобы отменить и перезагрузить интерфейс позже, все, что вам нужно сделать, это установить для параметра dispatch_block_t значение nil.

Возможно, более конкретно к вашей проблеме, этот пример куска кода имеет дело с Чтение данных из дескриптора , то есть либо на диске, либо в сети.

Как правило, вы должны использовать шаблон Call-Callback, который по существу получает фоновый поток, выполняет задачу, а при завершении вызывает другой блок, чтобы получить основной поток для обновления пользовательского интерфейса.

Надеюсь это поможет!

1
добавлено
спасибо за Ваш ответ.
добавлено автор IgnazioC, источник
спасибо за Ваш ответ. Вы уверены, что я могу установить self.task = nil , и это остановит выполнение блока? В этом случае мне нужно заново создать задачу для каждого объекта, который мне нужно показать ...
добавлено автор IgnazioC, источник
Да. В примере только очередь одного объекта. У вас может быть массив элементов задач и задать любой из тех задач, которые вы сочтете необходимыми.
добавлено автор Alex Smith, источник

You can check the view window property:

if (myLabel.window) {
 //update label
}

это избыточно if (label! = nil) , так как если метка равна nil, тогда все свойства метки также будут равно nil (или нулю), и их установка не приведет к возникновению исключения.

0
добавлено
Mobile Dev Jobs — вакансии и аналитика
Mobile Dev Jobs — вакансии и аналитика
6 187 участник(ов)

Публикуем вакансии и запросы на поиск работы по направлению iOS, Android, Xamarin и т.д. ВАЖНО: Правила публикации и правила канала: Ссылка – https://telegra.ph/Pravila-oformleniya-vakansij-i-rezyume-11-09-2

iOS Developers — русскоговорящее сообщество
iOS Developers — русскоговорящее сообщество
2 400 участник(ов)

Общаемся на темы, посвященным iOS-разработке, Swift, Objective-C, SDK, Rx, Cocoa и т.д.