Синхронные сети на основном потоке
Q: На Подключении iTunes я вижу много отчетов катастрофического отказа, говоря, что приложению «не удалось запуститься/приостановить/возобновить/завершить своевременно», со следом, указывающим на сетевой код. Что продолжается здесь?
A: На Подключении iTunes я вижу много отчетов катастрофического отказа, говоря, что приложению «не удалось запуститься/приостановить/возобновить/завершить своевременно», со следом, указывающим на сетевой код. Что продолжается здесь?
Это сторожевые отчеты катастрофического отказа тайм-аута, которые можно распознать позорным, «съел плохую еду» (0x8badf00d) код исключения, как показано в Перечислении 1.
Сторожевой тайм-аут перечисления 1 разрушает отчет
Exception Type: 00000020 Exception Codes: 0x8badf00d |
Наиболее распространенной причиной для сторожевых катастрофических отказов тайм-аута в сетевом приложении являются синхронные сети на основном потоке. Здесь существует четыре влияющих фактора:
синхронные сети — Это - то, где Вы выполняете сетевой запрос и блок, ожидающий ответа.
основной поток — Синхронные сети являются меньше, чем идеал в целом, но это вызывает определенные проблемы, если Вы делаете это на основном потоке. Помните, что основной поток ответственен за выполнение пользовательского интерфейса. При блокировании основного потока для какого-либо существенного количества времени пользовательский интерфейс становится неприемлемо безразличным.
долгие тайм-ауты — Если сеть просто уходит (например, пользователь находится на поезде, входящем в туннель), любой незаконченный сетевой запрос не перестанут работать, пока некоторый тайм-аут не истек. Тайм-ауты сети Most измеряются в минутах, означая, что блокированный запрос сети синхронизации на основном потоке может сохранить пользовательский интерфейс безразличным в течение многих минут за один раз.
Попытка избежать этой проблемы путем сокращения сетевого тайм-аута не является хорошей идеей. В некоторых ситуациях может потребоваться много секунд для сетевого запроса для следования, и если Вы всегда испытаете таймаут рано тогда, то Вы никогда не будете делать успехов вообще.
сторожевой таймер — для хранения пользовательского интерфейса быстро реагирующим, iOS включает сторожевой механизм. Если Ваше приложение не будет реагировать на определенные события пользовательского интерфейса (запуск, приостановите, возобновитесь, оконечный), своевременно, то сторожевой таймер уничтожит Ваше приложение и сгенерирует сторожевой отчет катастрофического отказа тайм-аута. Количество времени, которое сторожевой таймер дает Вам, формально не документируется, но это всегда - меньше, чем сетевой тайм-аут.
Один хитрый аспект этой проблемы - то, что это очень зависит от сетевой среды. Если Вы всегда будете тестировать свое приложение в Вашем офисе, где сетевое соединение хорошо, то Вы никогда не будете видеть этот тип катастрофического отказа. Однако, как только Вы начинаете развертывать свое приложение на конечных пользователях — кто выполнит его во всех видах сетевой среды — катастрофические отказы как это станут распространены.
Учитывая сторожевой таймер тайм-аут разрушает отчет, можно найти проблематичный код путем рассмотрения следа основного потока (распараллельте 0). Перечисление 2 является типичным примером.
Неработающее приложение перечисления 2, никакая булочка
Thread 0: 0 libSystem.B.dylib 0x00001668 mach_msg_trap + 20 1 libSystem.B.dylib 0x00003734 mach_msg + 44 2 CoreFoundation 0x0002296e CFRunLoopRunSpecific + 1150 3 CoreFoundation 0x000224da CFRunLoopRunInMode + 42 4 CFNetwork 0x0002b118 CFURLConnectionSendSynchronousRequest + 244 5 Foundation 0x000a45a2 +[NSURLConnection sendSynchronousRequest:returningResponse[…] 6 MyBadApp 0x000063f6 0x1000 + 21494 7 MyBadApp 0x0000630a 0x1000 + 21258 8 MyBadApp 0x00006ada 0x1000 + 23258 9 MyBadApp 0x00003618 0x1000 + 9752 10 MyBadApp 0x0000351e 0x1000 + 9502 11 MyBadApp 0x00003874 0x1000 + 10356 12 UIKit 0x00068568 -[UIViewController viewWillMoveToWindow:] + 76 13 UIKit 0x0000988a -[UIView(Hierarchy) _willMoveToWindow:withAncestorView:] + 126 14 UIKit 0x00005e6e -[UIView(Internal) _addSubview:positioned:relativeTo:] + 222 15 UIKit 0x00005d80 -[UIView(Hierarchy) addSubview:] + 16 16 MyBadApp 0x000028d2 0x1000 + 6354 17 UIKit 0x00003e58 -[UIApplication _performInitializationWithURL:payload:] + 336 18 UIKit 0x00003b22 -[UIApplication _runWithURL:payload:launchOrientation:] + 394 19 UIKit 0x0004f8c4 -[UIApplication handleEvent:withNewEvent:] + 1336 20 UIKit 0x0004f242 -[UIApplication sendEvent:] + 38 21 UIKit 0x0004ec8c _UIApplicationHandleEvent + 4772 22 GraphicsServices 0x00003b2c PurpleEventCallback + 660 23 CoreFoundation 0x00022d96 CFRunLoopRunSpecific + 2214 24 CoreFoundation 0x000224da CFRunLoopRunInMode + 42 25 UIKit 0x0000340a -[UIApplication _run] + 342 26 UIKit 0x00001954 UIApplicationMain + 636 27 MyBadApp 0x00002828 0x1000 + 6184 28 MyBadApp 0x000027f8 0x1000 + 6136 |
Смотря на кадры 5 и 6, Вы видите, что приложение выполнило синхронный сетевой вызов (+[NSURLConnection sendSynchronousRequest:returningResponse:error:]) который блокировал ожидание сети, таким образом вызвав ярость сторожевого таймера.
Как только Вы подтвердили, что эта проблема связана с Вашим сетевым кодом, существует два общих решения:
асинхронные сети — лучшее решение этой проблемы состоит в том, чтобы выполнить Ваш сетевой код асинхронно. Асинхронный сетевой код имеет много преимуществ, не в последнюю очередь которых то, что он позволяет Вам получить доступ к сети безопасно, не имея необходимость волноваться о потоках.
синхронные сети на вторичном потоке — Если предельно трудно выполнить Ваш сетевой код асинхронно (возможно, Вы работаете с большой переносимой кодовой базой, принимающей синхронные сети), можно избежать сторожевого таймера путем выполнения синхронного кода вторичного потока.
Существуют многочисленные примеры того, как выполнить сетевой код асинхронно, включая:
Кроме того, следующие ресурсы иллюстрируют один хороший метод для управления асинхронными сетями, а именно, с помощью NSOperation для инкапсуляции каждого сетевого запроса:
Наконец, важно понять, что синхронные сети могут быть скрыты позади уровней абстракции та маска опасность. Например, если Вы вызываете -[NSXMLParser -initWithContentsOfURL:] с «http» или «https» URL, NSXMLParser загрузит содержание того URL синхронно. Это могущественное удобный, но если Вы делаете это на основном потоке, Вы рискуете уничтожаться сторожевым таймером. Лучшая опция состоит в том, чтобы загрузить содержание URL к файлу асинхронно, и затем вызвать -[NSXMLParser -initWithContentsOfURL:] с «файлом» URL. То же применяется к многочисленным другим подпрограммам «WithContentsOfURL», как +[NSString stringWithContentsOfURL:encoding:error:], -[NSDictionary initWithContentsOfURL:], и т.д.
Другие типичные примеры скрытых синхронных сетей включают:
BSD DNS APIs — Традиционный BSD подпрограммы DNS, как
gethostbynameиgethostbyaddr, никогда не безопасны обратиться к основному потоку. Более современные подпрограммы какgetnameinfoиgetaddrinfoтолько безопасны, если Вы работаете исключительно с IP-адресами и не именами DNS (т.е. Вы указываетеAI_NUMERICHOSTиNI_NUMERICHOST, соответственно). Если необходимо разрешить адрес DNS вручную, используйте асинхронный API как CFHost или<dns_sd.h>. Еще лучше, если Вы планируете соединиться с сервером по имени, используйте асинхронный API, решающий и соединяющийся в одном хите (см. Технические Вопросы и ответы QA1652, 'Используя NSStreams Для соединения TCP Без NSHost' для примера этого).достижимость — достижимость платформы Конфигурации системы API (
<SystemConfiguration/SCNetworkReachability.h>) работает синхронно по умолчанию. Таким образом, на вид безвредные подпрограммы какSCNetworkReachabilityGetFlagsмогли уничтожить Вас сторожевым таймером. При использовании достижимости API необходимо использовать ее асинхронно. Это включает использованиеSCNetworkReachabilityScheduleWithRunLoopподпрограмма для планирования запросов достижимости на цикл выполнения. Пример кода 'Достижимость' показывает пример этого.
Для получения дополнительной информации об этой проблеме и ее решениях и высокоуровневом введении в сети на iOS в целом, можно смотреть представление 2010 года WWDC, «Сетевые Приложения для iPhone OS», часть 1 и 2 (сеансы 207 и 208).
Для получения дополнительной информации об отчетах катастрофического отказа iOS в целом, посмотрите Техническое примечание TN2151, 'Поняв и Анализируя Доклады Сбоя приложения iPhone OS'.
История версии документа
| Дата | Примечания |
|---|---|
| 23.12.2010 | Новый документ, описывающий риски выполнения синхронных сетей на основном потоке. |