Я немного не понимаю, как и когда использовать beginBackgroundTaskWithExpirationHandler
.
Apple показывает в своих примерах, как использовать его в applicationDidEnterBackground
делегате, чтобы получить больше времени для выполнения некоторой важной задачи, обычно сетевой транзакции.
Когда я смотрю на мое приложение, кажется, что большая часть моих сетевых вещей важна, и когда одно из них запущено, я хотел бы завершить его, если пользователь нажал кнопку домой.
Так принято ли / хорошей практикой оборачивать каждую сетевую транзакцию (и я не говорю о загрузке большого количества данных, это в основном короткий xml), beginBackgroundTaskWithExpirationHandler
чтобы быть в безопасности?
Ответы:
Если вы хотите, чтобы ваша сетевая транзакция продолжалась в фоновом режиме, вам нужно обернуть ее в фоновую задачу. Также очень важно, чтобы вы звонили,
endBackgroundTask
когда закончите, иначе приложение будет убито по истечении отведенного времени.Мои обычно выглядят примерно так:
У меня есть
UIBackgroundTaskIdentifier
свойство для каждой фоновой задачиЭквивалентный код в Swift
источник
Принятый ответ очень полезен и в большинстве случаев должен подойти, однако меня беспокоили две вещи:
Как отмечали многие люди, сохранение идентификатора задачи в качестве свойства означает, что он может быть перезаписан, если метод вызывается несколько раз, что приводит к задаче, которая никогда не будет корректно завершена до тех пор, пока ОС не будет принудительно завершена по истечении времени. .
Этот шаблон требует уникального свойства для каждого вызова,
beginBackgroundTaskWithExpirationHandler
что кажется обременительным, если у вас есть более крупное приложение с большим количеством сетевых методов.Чтобы решить эти проблемы, я написал синглтон, который заботится обо всей сантехнике и отслеживает активные задачи в словаре. Для отслеживания идентификаторов задач свойства не требуются. Кажется, работает хорошо. Использование упрощено до:
При желании, если вы хотите предоставить блок завершения, который делает что-то помимо завершения задачи (который встроен), вы можете вызвать:
Соответствующий исходный код доступен ниже (одноэлементные элементы исключены для краткости). Комментарии / отзывы приветствуются.
источник
typedef
CompletionBlock? Просто это:typedef void (^CompletionBlock)();
Вот класс Swift, который инкапсулирует выполнение фоновой задачи:
Самый простой способ его использования:
Если вам нужно дождаться обратного вызова делегата перед завершением, используйте что-то вроде этого:
источник
begin
метода, но легко увидеть, как добавить эту функцию.Как отмечено здесь и в ответах на другие вопросы SO, вы НЕ хотите использовать
beginBackgroundTask
только тогда, когда ваше приложение перейдет в фоновый режим; напротив, вы должны использовать фоновую задачу для любой трудоемкой операции, выполнение которой вы хотите гарантировать, даже если приложение действительно перейдет в фоновый режим.Поэтому ваш код, скорее всего, в конечном итоге приправлены повторений одного и того же шаблонного кода для вызова
beginBackgroundTask
иendBackgroundTask
слаженно. Чтобы предотвратить это повторение, безусловно, разумно захотеть упаковать шаблон в некую единую инкапсулированную сущность.Мне нравятся некоторые из существующих ответов для этого, но я думаю, что лучший способ - использовать подкласс Operation:
Вы можете поставить операцию в очередь на любую OperationQueue и управлять этой очередью по своему усмотрению. Например, вы можете досрочно отменить любые существующие операции в очереди.
Если у вас есть несколько дел, вы можете связать несколько операций фоновых задач. Зависимости поддержки операций.
Очередь операций может (и должна) быть фоновой очередью; таким образом, нет необходимости беспокоиться о выполнении асинхронного кода внутри вашей задачи, потому что операция - это асинхронный код. (Действительно, нет смысла выполнять другой уровень асинхронного кода внутри операции, поскольку операция завершится до того, как этот код сможет даже начаться. Если бы вам нужно было это сделать, вы бы использовали другую операцию.)
Вот возможный подкласс Operation:
Должно быть очевидно, как это использовать, но если это не так, представьте, что у нас есть глобальная OperationQueue:
Итак, для типичного трудоемкого пакета кода мы бы сказали:
Если ваш трудоемкий пакет кода можно разделить на этапы, вы можете отказаться раньше, если ваша задача будет отменена. В таком случае просто преждевременно вернитесь из укупорки. Обратите внимание, что ваша ссылка на задачу из закрытия должна быть слабой, иначе вы получите цикл сохранения. Вот искусственная иллюстрация:
На случай, если вам нужно выполнить очистку в случае преждевременной отмены самой фоновой задачи, я предоставил дополнительное
cleanup
свойство обработчика (не используется в предыдущих примерах). Некоторые другие ответы подверглись критике за отсутствие этого.источник
Я реализовал решение Джоэла. Вот полный код:
.h файл:
.m файл:
источник