NSDefaultRunLoopMode против NSRunLoopCommonModes

114

Всякий раз , когда я пытаюсь загрузить большой файл с фоновым UIScrollView, MPMapViewили что - то, процесс загрузки получает остановился , как только сенсорный экран iPhone. К счастью, в прекрасном сообщении в блоге Йорна предлагается альтернативный вариант - использование NSRunLoopCommonModesдля подключения.

Это дает мне возможность подробно изучить два режима, NSDefaultRunLoopMode и NSRunLoopCommonModes, но в документе Apple не объясняется любезно, кроме как

NSDefaultRunLoopMode

Режим работы с источниками ввода, отличными от объектов NSConnection. Это наиболее часто используемый режим цикла выполнения.

NSRunLoopCommonModes

Объекты, добавленные в цикл выполнения, использующие это значение в качестве режима, отслеживаются всеми режимами цикла выполнения, которые были объявлены как член набора «общих» режимов; подробности см. В описании CFRunLoopAddCommonMode.

CFRunLoopAddCommonMode

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

Кто-нибудь может объяснить эти двое на человеческом языке?

Stkim1
источник

Ответы:

204

Цикл выполнения - это механизм, который позволяет системе пробуждать спящие потоки, чтобы они могли управлять асинхронными событиями. Обычно, когда вы запускаете поток (за исключением основного потока), есть возможность запускать поток в цикле выполнения или нет. Если поток выполняет какую-либо операцию сортировки или длительную операцию без взаимодействия с внешними событиями и без таймеров, вам не нужен цикл выполнения, но если вашему потоку необходимо реагировать на входящие события, его следует присоединить к циклу выполнения, чтобы будить нить, когда приходят новые события. Это случай NSURLConnectionсгенерированных потоков, поскольку они просыпаются только при входящих событиях (из сети).

Каждый поток может быть связан с несколькими циклами выполнения или может быть связан с конкретным циклом выполнения, который можно настроить для работы в разных режимах. «Режим цикла выполнения» - это соглашение, используемое ОС для установления некоторых правил, определяющих, когда доставлять определенные события или собирать их для доставки позже.

Обычно все циклы выполнения устанавливаются в «режим по умолчанию», который устанавливает способ управления входными событиями по умолчанию. Например: как только происходит событие перетаскивания мышью (Mac OS) или касания (в iOS), тогда режим для этого цикла выполнения устанавливается на отслеживание событий; это означает, что поток не будет разбужен при новых сетевых событиях, но эти события будут доставлены позже, когда событие пользовательского ввода завершится и цикл выполнения снова перейдет в режим по умолчанию; очевидно, что архитекторы ОС сделали выбор в пользу приоритета пользовательских событий, а не фоновых событий.

Если вы решите изменить режим цикла выполнения для вашего NSURLConnectionпотока, используя scheduleInRunLoop:forModes:, то вы можете назначить поток специальному режиму цикла выполнения , а не конкретному циклу выполнения по умолчанию. Вызываемый специальный псевдорежим NSRunLoopCommonModesиспользуется многими источниками ввода, включая отслеживание событий. Например, присвоение NSURLConnectionэкземпляра 'общему режиму означает связывание его событий с «режимом отслеживания» в дополнение к «режиму по умолчанию». Одним из преимуществ / недостатков связывания потоков NSRunLoopCommonModesявляется то, что поток не будет заблокирован событиями касания.

К обычным режимам можно добавлять новые, но это довольно низкоуровневая операция.

В заключение хочу добавить несколько примечаний:

  • Обычно нам нужно использовать набор изображений или миниатюр, загруженных из сети, в виде таблицы. Мы можем подумать, что загрузка этих изображений из сети во время прокрутки табличного представления может улучшить взаимодействие с пользователем (поскольку мы могли видеть изображения во время прокрутки), но это не дает преимуществ, поскольку плавность прокрутки может сильно пострадать. В этом примере NSURLConnectionцикл выполнения использовать не следует; было бы лучше использовать UIScrollViewметоды делегата, чтобы определить, когда прокрутка завершена, а затем обновить таблицу и загрузить новые элементы из сети;

  • Вы можете рассмотреть возможность использования GCD, который поможет вам «защитить» ваш код от проблем управления циклом выполнения. В приведенном выше примере вы можете рассмотреть возможность добавления сетевых запросов в настраиваемую последовательную очередь.

viggio24
источник
9
Дорогой Viggio24, большое спасибо за это чистое и точное объяснение. Я бы попросил Apple включить ваш комментарий в их руководство по API. ;)
Stkim1
7
Ответ viggio24 идеален. Тем, кому интересно, я хотел бы указать, что сеанс 208 (Сетевые приложения для iPhone OS, часть 2) из WWDC 2010 содержит введение в циклы выполнения. Если вам интересно, посмотрите. Надеюсь, поможет.
Lorenzo B,
19
Просто замечание для себя: NSRunLoopCommonModesразрешает событие таймера при прокрутке UIScrollView. NSDefaultRunLoopModeзапретить таймер при прокрутке.
eonil
2
Мне показался очень интересным комментарий об обновлении режима прокрутки, поскольку в нем упоминается очень сложная тема. Просто чтобы добавить больше подробностей об этом: когда вы устанавливаете режим для NSURLConnection, это влияет только на выполнение обратных вызовов делегатов. Я понимаю, что обновление scrollView здесь может привести к проблемам с производительностью, но почему это происходит? Если ответ заключается в том, что изображение должно быть загружено в память, вы можете сделать это, записав его в графический контекст на заднем плане, и после этого обновить слой основного потока представления. это звучит разумно?
небилло