Понимание NSRunLoop
Может кто-нибудь объяснить, для чего NSRunLoop
? так как я знаю NSRunLoop
это что-то, связанное с NSThread
право? Итак, предположим, что я создаю тему как
NSThread* th=[[NSThread alloc] initWithTarget:self selector:@selector(someMethod) object:nil];
[th start];
-(void) someMethod
{
NSLog(@"operation");
}
так после того как этот поток закончит свою работу правильно? зачем использовать RunLoops
или где использовать? из документов Apple я что-то читал, но мне это не понятно, поэтому, пожалуйста, объясните как можно проще
6 ответов
Цикл выполнения - это абстракция, которая (помимо прочего) предоставляет механизм для обработки системных входных источников (сокетов, портов, файлов, клавиатуры, мыши, таймеров и т. Д.).
Каждый NSThread имеет свой собственный цикл выполнения, доступ к которому можно получить с помощью метода currentRunLoop.
Как правило, вам не нужно напрямую обращаться к циклу выполнения, хотя есть некоторые (сетевые) компоненты, которые могут позволить вам указать, какой цикл выполнения они будут использовать для обработки ввода-вывода.
Цикл выполнения для данного потока будет ожидать, пока один или несколько его входных источников не получат какие-либо данные или событие, а затем запустит соответствующий обработчик (и) ввода для обработки каждого входного источника, который "готов".
После этого он вернется в свой цикл, обрабатывая входные данные из различных источников и "спя", если не нужно выполнять никаких действий.
Это описание довольно высокого уровня (пытаясь избежать слишком большого количества деталей).
РЕДАКТИРОВАТЬ
Попытка ответить на комментарий. Я разбил его на куски.
- это означает, что я могу получить доступ / запустить только для запуска цикла внутри потока, верно?
В самом деле. NSRunLoop не является потокобезопасным, и к нему следует обращаться только из контекста потока, выполняющего цикл.
- Есть ли простой пример, как добавить событие для запуска цикла?
Если вы хотите отслеживать порт, вы просто добавили бы этот порт в цикл выполнения, а затем цикл запуска отслеживал бы активность этого порта.
- (void)addPort:(NSPort *)aPort forMode:(NSString *)mode
Вы также можете явно добавить таймер с помощью
- (void)addTimer:(NSTimer *)aTimer forMode:(NSString *)mode
- что значит, что он потом вернется в свой цикл?
Цикл выполнения будет обрабатывать все готовые события на каждой итерации (в соответствии с его режимом). Вам нужно будет ознакомиться с документацией, чтобы узнать о режимах работы, поскольку это немного выходит за рамки общего ответа.
- цикл запуска неактивен, когда я запускаю поток?
В большинстве приложений основной цикл выполнения будет выполняться автоматически. Однако вы несете ответственность за запуск цикла выполнения и ответ на входящие события для потоков, которые вы вращаете.
- Можно ли добавить некоторые события в цикл выполнения потока вне потока?
Я не уверен, что вы имеете в виду здесь. Вы не добавляете события в цикл выполнения. Вы добавляете источники ввода и источники таймера (из потока, которому принадлежит цикл выполнения). Затем цикл запуска отслеживает их активность. Конечно, вы можете обеспечить ввод данных из других потоков и процессов, но ввод будет обрабатываться циклом выполнения, который отслеживает эти источники в потоке, в котором выполняется цикл выполнения.
- Означает ли это, что иногда я могу использовать цикл выполнения, чтобы заблокировать поток на время
В самом деле. Фактически, цикл выполнения будет "оставаться" в обработчике событий до тех пор, пока этот обработчик не вернется. Вы можете увидеть это в любом приложении достаточно просто. Установите обработчик для любого действия ввода-вывода (например, нажатие кнопки), которое спит. Вы будете блокировать основной цикл выполнения (и весь пользовательский интерфейс) до завершения этого метода.
То же самое относится к любому циклу выполнения.
Я предлагаю вам прочитать следующую документацию по циклам выполнения:
https://developer.apple.com/documentation/foundation/nsrunloop
и как они используются в потоках:
Циклы выполнения - это то, что отделяет интерактивные приложения от инструментов командной строки.
- Инструменты командной строки запускаются с параметрами, выполняют их команду, а затем завершаются.
- Интерактивные приложения ждут ввода пользователя, реагируют, затем возобновляют ожидание.
Они позволяют подождать, пока пользователь нажмет и ответит соответственно, подождать, пока вы не получите завершение и обработчик и применить его результаты, подождать, пока вы получите таймер и выполнить функцию. Если у вас нет runloop, то вы не можете прослушивать / ждать пользовательских нажатий, вы не можете ждать, пока не произойдет сетевой вызов, вы не сможете проснуться через x минут.
Также из этого комментария:
Фоновые потоки не имеют своих собственных циклов выполнения, но вы можете просто добавить их. Например, AFNetworking 2.x сделал это. Это была опробованная и настоящая методика для NSURLConnection или NSTimer в фоновых потоках, но мы сами больше этого не делаем, поскольку новые API избавляют от необходимости делать это. Но, похоже, что URLSession выполняет, например, простой запрос, выполняя [см. Левую панель изображения] обработчики завершения в главной очереди, и вы можете видеть, что он имеет цикл выполнения в фоновом потоке
Runloops - что-то вроде коробки, где вещи просто случаются.
В основном в Runloop вы идете обрабатывать некоторые события, а затем возвращаетесь. ИЛИ вернитесь, если он не обрабатывает события до истечения времени ожидания. Вы можете сказать, что это похоже на асинхронные NSURLConnections, Обработка данных в фоновом режиме, не влияя на ваш текущий цикл, и в то же время вам требуются данные синхронно. Что может быть сделано с помощью Runloop, который контролирует асинхронную связь NSURLC и предоставляет данные во время вызова. Вы можете использовать Runloop следующим образом:
NSDate *loopUntil = [NSDate dateWithTimeIntervalSinceNow:0.1];
while (YourBoolFlag && [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate:loopUntil])
loopUntil = [NSDate dateWithTimeIntervalSinceNow:0.1];
в этом Runloop он будет работать до тех пор, пока вы не завершите некоторые другие ваши работы и не установите для YourBoolFlag значение false.
Точно так же вы можете использовать их в потоках.
Надеюсь, это поможет вам.
iOS RunLoop
RunLoop(EventLoop, Looper)
является реализацией шаблона EventLoop (цикл обработки событий). Он основан наNSRunLoop
(который является оберткойCFRunLoopRef
)
Циклы выполнения являются частью фундаментальной инфраструктуры, связанной с потоками. Цикл выполнения — это цикл обработки событий, который используется для планирования работы и координации получения входящих событий. Цель цикла выполнения состоит в том, чтобы держать ваш поток занятым, когда есть работа, и переводить его в спящий режим, когда ее нет.
Один поток может иметь один RunLoop в одном режиме. Будут обрабатываться только события с этим режимом, все остальные будут ждать запуска RunLoop в этом режиме.
RunLoop — это механизм (основанный на цикле (for, while)), который перемещает запланированную задачу (например, очередь обратного вызова) в поток (стек потоков). RunLoop работает(), когда стек потоков пуст.
event processing loop
когда RunLoop между.entry
и.exit
. Во время этого RunLoop обрабатывает все запланированные задачи в определенном режиме. Все остальные режимы с собственными очередями будут управляться после
Приложение по умолчанию имеетmain thread
сRunLoop(main loop)
. В остальных случаях вы должны создать его вручную
main run loop
отвечает за сливmain queue
в приложении.
//Run loop for the current thread
RunLoop.current
//Run loop of the main thread.
RunLoop.main

Режим
Режим цикла выполнения — это набор источников ввода и таймеров, которые необходимо отслеживать, а также набор наблюдателей цикла выполнения, о которых нужно уведомлять.
режимы:
- по умолчанию - используется по умолчанию
- отслеживание — например, при прокрутке UITableView
scrollViewDidScroll
- общий (это псевдорежим, например [по умолчанию, отслеживание])
-
<custom>
- вы можете создать свой собственный режим
//current input mode
RunLoop.current.currentMode
Например:
-
UIView.draw(_ rect:)
, действие кнопки... использует - NSObject.perform(_:with:afterDelay:) использует
- использует
-
Timer.scheduledTimer
использует . Вот почему, когда происходит прокрутка пользовательского интерфейса (в режиме отслеживания), ваш таймер не срабатывает (в режиме по умолчанию). Чтобы исправить это, используйте общий режим -RunLoop.main.add(timer, forMode: .common)
- Объединить
RunLoop.main
противDispatchQueue.main
(.receive(on:, options:)
). RunLoop.main используетRunLoop.perform(_:)
который используетdefault mode
, DispatchQueue.main используетDispatchQueue.main.async
который используетcommon mode
источники ввода и таймеры
Цикл выполнения получает события:
Input sources
- сообщения об асинхронных событиях (при запуске)- На основе порта — из другого потока или процесса. сигнализируется ядром автоматически
- Пользовательские источники ввода — события, инициированные пользователем — действия пользователя, сетевые события. необходимо сигнализировать вручную из другого потока
-
performSelector: onThread
Timer sources
- таймеры синхронных событий (в определенное время)
Их можно добавить в несколько режимов
наблюдатели
отслеживать изменения состояния RunLoop
Создать цикл выполнения
создать новый поток, настроить RunLoop и запустить поток
- создать RunLoopRunLoop.current
- Цикл выполнения должен иметь по крайней мере один источник ввода или таймер для мониторинга RunLoop.add(_ timer: Timer, режим forMode: RunLoop.Mode) RunLoop.add(_ aPort: Port, режим forMode: RunLoop.Mode)
- запустить RunLoopRunLoop.run()
let thread = Thread {
//1. create RunLoop
//create a new one or return existing run loop for current thread
//use RunLoop.current instead of RunLoop()
let customRunLoop = RunLoop.current
//add observer for current RunLoop for cpecufic mode
CFRunLoopAddObserver(CFRunLoopGetCurrent(), customObserver, CFRunLoopMode.commonModes)
//2. A run loop must have at least one input source or timer to monitor
let timer = Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { (timer) in
//.default mode
}
customRunLoop.add(timer, forMode: .default)
//3. run RunLoop
//If no input sources or timers are attached to the run loop, this method exits immediately
//infinite loop that processes data from the run loop’s input sources and timers.
//calls RunLoop.run(mode:.default before:)
customRunLoop.run()
//------
//create custom mode
let customRunLoopMode = RunLoop.Mode("customeMode")
//2. A run loop must have at least one input source or timer to monitor
//Will be called when previous RunLoop.run() is done(no input sources or timers) - exit from loop
let timer2 = Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { (timer) in
//"customeMode" mode
}
customRunLoop.add(timer2, forMode: customRunLoopMode)
//3. run RunLoop
let isInputSourcesOrTimers = customRunLoop.run(mode: customRunLoopMode, before: Date.distantFuture)
}
thread.start()
let customObserver = CFRunLoopObserverCreateWithHandler(nil, CFRunLoopActivity.allActivities.rawValue , true, 0) { observer, activity in
switch (activity) {
case .entry:
break
case .beforeTimers:
break
case .beforeSources:
break
case .beforeWaiting:
break
case .afterWaiting:
break
case .exit:
break
case .allActivities:
break
default:
break
}
}
Циклы выполнения являются частью фундаментальной инфраструктуры, связанной с потоками. Цикл выполнения - это цикл обработки событий, который вы используете для планирования работы и координации приема входящих событий. Цель цикла выполнения состоит в том, чтобы сохранить ваш поток занятым, когда есть работа, и перевести ваш поток в спящий режим, когда его нет.
Наиболее важной особенностью CFRunLoop является CFRunLoopModes. CFRunLoop работает с системой "Run Loop Sources". Источники регистрируются в цикле выполнения для одного или нескольких режимов, и сам цикл выполнения выполняется для выполнения в данном режиме. Когда событие поступает в источник, оно обрабатывается циклом выполнения, только если режим источника соответствует текущему режиму цикла выполнения.
Swift
let runLoop = RunLoop.current
Obj-c
NSRunLoop * runloop = [NSRunLoop currentRunLoop];
Цикл выполнения — это цикл обработки событий, который используется для непрерывного мониторинга и обработки входных событий и назначения их соответствующим целям для обработки.