Intereting Posts
Приложение Paring iOS для Samsung TV в качестве пульта дистанционного управления Основные данные iOS: запутаны основные данные и база данных Проблема с CFBundleIdentifier Collision Потяните, чтобы обновить Crash my application Значение экспоненты не отображается, что мне делать? Найти показанный сверху вид UIActionSheet Установить нижний колонтитул стола во время загрузки требуемых ячеек показать программные изменения раскадровки Xamarin iOS Перейдите к настройкам Как установить пользовательские правила, такие как уровень сложности для GameCenter, чтобы найти совпадение Невозможно выполнить анимацию изменения цвета CAGradientLayer Как воспроизводить аудио из пользовательской позиции UISlider? Admob межстраничные объявления – не может быть промежуточным. Он не готов Установить размер сцены для просмотра размера? Время GPS местоположение в фоновом режиме (iphone)

Блокировка переключения в iOS

У меня проблемы с несколькими потоками. Вот ситуация:

Я делаю асинхронные запросы на бэкэнд и, в некоторых случаях, должен их отменять. Отмена запросов происходит в отдельном потоке. Все запросы на бэкэнд отменены, и, когда я выхожу из экрана, несколько экземпляров класса освобождаются.

Когда у меня есть запрос на отмену заказа, все работает нормально. Однако иногда метод cancel вызывается, когда я уже посередине метода финиша (который требует немного времени из-за декодирования и преобразования данных). В таких случаях приложение выходит из строя, отправляется сообщение на освобожденный экземпляр. Это не исключение, которое можно легко кэшировать, я получаю сбой даже тогда, когда проверяю наличие экземпляров строки раньше. Собственно, если я правильно понимаю, экземпляр класса, в котором находится финиш и метод отмены, освобождается. Nevermind, проблема в том, что нить переключается в середине метода финиша, и мне нужно это предотвратить.

Мой вопрос: есть ли способ блокировать переключение потока в середине метода? Объявить этот метод в целом (транзакцию)? Или есть еще одно стандартное решение для такой проблемы?

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

EDIT: Отмена

for (XXX *request in [XXX sharedQueue].operations) { [request setDelegate:nil]; [request setDownloadProgressDelegate:nil]; [request setUploadProgressDelegate:nil]; [request setQueue:nil]; [request cancel]; } 

XXX – это класс, используемый для запросов.

Порядок методов

Вот порядок, в котором используются методы (в случае ошибки). Handler – это класс для обработки запросов.

 Handler 1: make request Handler 1: finish begin Handler 2: cancel begin Handler 2: cancel end Handler 2: dealloc Handler 1: dealloc Handler 1: finish middle 

Обработчики 1 и 2 являются двумя экземплярами класса. Первый из них освобожден в середине метода финиша, поэтому в конце этого я получаю сбой. Расселение это нормально, потому что после отмены я перехожу к другому виду и в основном все освобождается.

Мои идеи для решения – либо каким-то образом предотвратить возврат к методу финиша, либо выполнить весь метод завершения перед переключением потока. К сожалению, я понятия не имею, как один из них может быть реализован.

Следующий подход может помочь вам:

  1. добавьте флаг контроллеру, управляющему всеми запросами;

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

     completionBlock:^() { @synchronized(self.myFinishFlag) { ... } } 
  3. в вашем методе отмены сделайте следующее:

     -(void)userCancelledRequests:(id)sender { @synchronized(self.myFinishFlag) { ... } } 

Это задержит выполнение «bodyCancelledRequests», body if a finish block is currently running (ie, locking Е. body if a finish block is currently running (ie, locking self.myFinishFlag`).

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

Похоже, что метод cancel класса XXX неправильно реализован.

Предположим, что существует асинхронная операция типа XXX которая отвечает на сообщение cancel . Чтобы функционировать надежно, должны выполняться следующие требования:

  1. Сообщение cancel может быть отправлено клиентом из любого потока.

  2. Сообщение cancel может быть отправлено в любое время и несколько раз.

  3. Когда операция получает сообщение cancel , она останавливает ее асинхронную задачу и правильно очищает ее в следующей «точке отмены». Примечание: это может происходить асинхронно по отношению к методу cancel . Реализация должна быть «потокобезопасной»!

  4. Получатель должен уведомить делегата о том, что он был отменен (например, в обработчике сбоев).

  5. Кроме того, не должно быть необходимости, чтобы вызывающий пользователь каким-либо образом сбросил делегат (-ы) или подготовил получателя до отправки сообщения об cancel .

Эти требования должны выполняться путем реализации класса XXX .

Теперь предположим, что у вас есть метод внутренней finish для этой операции и позволяет предположить, что это последний метод, который будет выполняться операцией. Когда этот метод выполняется, и когда операция получает сообщение cancel одновременно , реализация должна гарантировать, что cancel не действует, поскольку уже слишком поздно: последняя возможность отменить операцию была передана. Есть несколько способов, как это сделать.

Если это так, то следующий код должен должным образом отменить ваши операции:

 for (XXX *request in [XXX sharedQueue].operations) { [request cancel]; } 

Редактировать:

Пример реализации «NSOperation like» метода cancel и finish :

Заметка:

Доступ к иварам должен быть синхронизирован ! Доступ к Ivars будет осуществляться различными методами. Все обращения будут сериализованы через приватную очередь последовательной отправки с именем «sync_queue».

 - (void) cancel { dispatch_async(self.sync_queue, ^{ if (_isCancelled || _isFinished) { return; } [self.task cancel]; // will sets _isCancelled to YES [self finish]; // will set _isFinished to YES }); } - (void) finish { assert(<execution context equals sync_queue>); if (_isFinished) return; completionHandler_t onCompletion = self.completionHandler; if (onCompletion) { id result = self.result; dispatch_async(dispatch_get_global_queue(0, 0), ^{ onCompletion(result); }); }; self.completionHandler = nil; self.task = nil; self.isExecuting = NO; self.isFinished = YES; } 

Лучший способ блокировки – выполнить все операции в выделенной очереди. Когда вы хотите отменить, сделайте следующее:

 dispatch_async(_someQueue, ^{ for (XXX *request in [XXX sharedQueue].operations) { [request setDelegate:nil]; [request setDownloadProgressDelegate:nil]; [request setUploadProgressDelegate:nil]; [request setQueue:nil]; [request cancel]; } }); 

Затем, когда завершен обратный вызов, вы можете сделать это:

 dispatch_async(_someQueue, ^{ if ([request isCancelled] == NO) { // Process request } }); 

Пока _someQueue не помечен как параллельная очередь, это должно сделать трюк.