Объединение асинхронных вызовов в синхронный поток блокировки?
Я пишу модуль iOS, который в настоящее время отправляет электронное письмо асинхронно (используя делегатов). Он использует SKPSMTPMessage
, который отлично работает. Моя проблема заключается в том, что клиент хочет, чтобы код полностью блокировал поток до тех пор, пока сообщение не было отправлено (или не было отправлено). Поэтому они в основном запрашивают синхронное решение, когда в настоящее время он попытается отправить электронное письмо, а затем вернуться из этого блока кода до отправки сообщения электронной почты.
Поэтому вместо того, чтобы пытаться переписать код SKPSMTPMessage
синхронным способом (для него, похоже, не существует каких-либо синхронных параметров), я надеюсь найти способ переноса этого блока асинхронного кода в свой собственный нить и, возможно, сделать основной поток, ожидая его полного завершения (делегаты и все).
Я пробовал несколько разных методов с помощью NSOperation
и NSThread
, но, возможно, я не делаю что-то правильно, потому что каждый раз, когда я пытаюсь заблокировать основной поток, асинхронные вызовы делегатов все равно никогда не заканчиваются (они вернуться на главную нить или что-то еще?).
Любая информация или даже другие идеи оценены.
PS ~ Я понимаю, что это немного назад. В большинстве случаев асинхронность, по-видимому, является способом выхода, но это особый случай, и у клиента есть свои причины для его желания.
EDIT: Спасибо за все входные данные. Как было предложено одним из ответов, я закончил тем, что просто использовал цикл while, который ждал возвращения делегатов, но пусть runLoop будет продолжать так же:
while( ![messageDelegate hasFinishedOrFailed] ){
// Allow the run loop to do some processing of the stream
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
}
Ответы
Ответ 1
Я не верю, что есть способ сделать это без изменения SKPSMTPMessage
. Класс фактически не использует отдельные потоки; вместо этого он использует NSStream
совместно с циклом запуска потока, чтобы избежать блокировки:
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSRunLoopCommonModes];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSRunLoopCommonModes];
Поток действует как источник входного сигнала для цикла ; цикл выполнения затем может обрабатывать другие события до тех пор, пока что-то не произойдет с потоком, и в этот момент поток уведомляет его делегата. Все все еще происходит на исходной (основной) теме; если вы попытаетесь заблокировать его самостоятельно, вы также заблокируете цикл выполнения, и он не сможет ничего сделать с потоком.
Другие уже указали, что блокирование основного потока - плохая идея; кроме проблем с UX, система может прекратить любое приложение, которое не реагирует на события в течение слишком длительного периода. Тем не менее, вы можете поместить всю настройку сообщения в фоновый режим, предоставив ей собственный цикл запуска потока для работы и заблокировать основной поток, используя GCD. К сожалению, я не могу придумать, как делегат может сигнализировать, что это сделано без опроса.
dispatch_queue_t messageQueue;
messageQueue = dispatch_queue_create("com.valheru.messageQueue", NULL);
// dispatch_sync blocks the thread on which it called
dispatch_sync(messageQueue, ^{
[messageManager tryToDeliverMessage];
});
dispatch_release(messageQueue);
Где tryToDeliverMessage
выглядит примерно так:
- (void) tryToDeliverMessage {
// Create the message and let it run...
// Poll a flag on the delegate
while( ![messageDelegate hasFinishedOrFailed] ){
// Allow the run loop to do some processing of the stream
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
}
return;
}
Ответ 2
Я бы попробовал использовать семафоры отправки. На странице man для dispatch_semaphore_create(3)
:
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
dispatch_async(queue, ^{
foo();
dispatch_semaphore_signal(sema);
});
bar();
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
dispatch_release(sema);
sema = NULL;
Вызов dispatch_semaphore_wait()
будет заблокирован до завершения вызова dispatch_semaphore_signal()
.
Ответ 3
Исправьте меня, если я неправильно понимаю ограничения, но конечная цель - не допустить перехода пользователя после отправки электронной почты? Если это так, то, возможно, добавление какого-то полного обзора прогресса на экране, поскольку подобие окна будет хорошим решением. Отключите взаимодействие с пользователем и удалите представление, когда вы получите успешный или неудачный обратный вызов через методы делегата.
Ответ 4
while (! [messageDelegate hasFinishedOrFailed]) { // Разрешить циклу выполнения выполнять некоторую обработку потока [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 1]];
}
это приведет к тому, что представление таблицы может не прокручиваться иногда, а представление предупреждений не может отклонить