Технические вопросы и ответы QA1693

Синхронные сети на основном потоке

Q: На Подключении iTunes я вижу много отчетов катастрофического отказа, говоря, что приложению «не удалось запуститься/приостановить/возобновить/завершить своевременно», со следом, указывающим на сетевой код. Что продолжается здесь?

A: На Подключении iTunes я вижу много отчетов катастрофического отказа, говоря, что приложению «не удалось запуститься/приостановить/возобновить/завершить своевременно», со следом, указывающим на сетевой код. Что продолжается здесь?

Это сторожевые отчеты катастрофического отказа тайм-аута, которые можно распознать позорным, «съел плохую еду» (0x8badf00d) код исключения, как показано в Перечислении 1.

  Сторожевой тайм-аут перечисления 1 разрушает отчет

Exception Type: 00000020 Exception Codes: 0x8badf00d

Наиболее распространенной причиной для сторожевых катастрофических отказов тайм-аута в сетевом приложении являются синхронные сети на основном потоке. Здесь существует четыре влияющих фактора:

  1. синхронные сети — Это - то, где Вы выполняете сетевой запрос и блок, ожидающий ответа.

  2. основной поток — Синхронные сети являются меньше, чем идеал в целом, но это вызывает определенные проблемы, если Вы делаете это на основном потоке. Помните, что основной поток ответственен за выполнение пользовательского интерфейса. При блокировании основного потока для какого-либо существенного количества времени пользовательский интерфейс становится неприемлемо безразличным.

  3. долгие тайм-ауты — Если сеть просто уходит (например, пользователь находится на поезде, входящем в туннель), любой незаконченный сетевой запрос не перестанут работать, пока некоторый тайм-аут не истек. Тайм-ауты сети Most измеряются в минутах, означая, что блокированный запрос сети синхронизации на основном потоке может сохранить пользовательский интерфейс безразличным в течение многих минут за один раз.

    Попытка избежать этой проблемы путем сокращения сетевого тайм-аута не является хорошей идеей. В некоторых ситуациях может потребоваться много секунд для сетевого запроса для следования, и если Вы всегда испытаете таймаут рано тогда, то Вы никогда не будете делать успехов вообще.

  4. сторожевой таймер — для хранения пользовательского интерфейса быстро реагирующим, 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:], и т.д.

Другие типичные примеры скрытых синхронных сетей включают:

Для получения дополнительной информации об этой проблеме и ее решениях и высокоуровневом введении в сети на iOS в целом, можно смотреть представление 2010 года WWDC, «Сетевые Приложения для iPhone OS», часть 1 и 2 (сеансы 207 и 208).

Для получения дополнительной информации об отчетах катастрофического отказа iOS в целом, посмотрите Техническое примечание TN2151, 'Поняв и Анализируя Доклады Сбоя приложения iPhone OS'.



История версии документа


ДатаПримечания
23.12.2010

Новый документ, описывающий риски выполнения синхронных сетей на основном потоке.