Как ожидание успешно для блока, который должен быть выполнен при следующей отправке? - PullRequest
0 голосов
/ 15 января 2020
import XCTest
@testable import TestWait

class TestWait: XCTestCase {

    func testX() {
        guard Thread.isMainThread else {
            fatalError()
        }
        let exp = expectation(description: "x")
        DispatchQueue.main.async {
            print("block execution")
            exp.fulfill()
        }
        print("before wait")
        wait(for: [exp], timeout: 2)
        print("after wait")
    }
}

Вывод:

before wait
block execution
after wait

Я пытаюсь рационализировать последовательность отпечатков. Вот что я думаю:

  1. тест запущен на главном сервере
  2. он отправляет блок из основного потока, но так как отправка происходит из основного потока, то выполнение блока должно ждать до тех пор, пока не будет выполнен текущий блок
  3. «до того как будет напечатано ожидание»
  4. we wait, чтобы ожидание было выполнено. Это ожидание спит текущий поток, ie основной поток в течение 2 секунд. так как в мире ожидания успешны, хотя мы все еще не отправили из основного потока. Я имею в виду "после ожидания" еще не напечатано! Таким образом, мы все еще должны быть в главной теме. Следовательно, «выполнение блока» никогда не может произойти.

Что не так с моим объяснением? Я предполагаю, что это должно быть что-то с тем, как wait реализовано

Ответы [ 3 ]

1 голос
/ 15 января 2020

Если есть сомнения, посмотрите на исходный код!

https://github.com/apple/swift-corelibs-xctest/blob/ab1677255f187ad6eba20f54fc4cf425ff7399d7/Sources/XCTest/Public/Asynchronous/XCTWaiter.swift#L358

Весь код ожидания не прост, но фактическое ожидание сводится к:

_ = runLoop.run(mode: .default, before: Date(timeIntervalSinceNow: timeIntervalToRun))

Не следует думать об ожиданиях с точки зрения потоков , а с точки зрения очередей . С помощью RunLoop.current.run() вы в основном указываете текущему коду начать выполнение других элементов в очереди.

1 голос
/ 19 января 2020

wait(for:timeout:) из XCTestCase не похож на GCD-группу / семафор wait функций, с которыми вы, вероятно, знакомы.

Когда вы звоните wait(for:timeout:), очень как и вызовы GCD wait, он не будет возвращаться до тех пор, пока не истечет время ожидания или не будут решены ожидания. Но, в случае XCTestCase и в отличие от вариаций GCD, внутри wait(for:timeout:) он зацикливается, многократно вызывая run(mode:before:) до тех пор, пока ожидания не будут разрешены или не истечет время ожидания. Это означает, что хотя testX не будет продолжаться до тех пор, пока не будет удовлетворен wait, вызовы run(mode:before:) позволят запуску l oop продолжать обрабатывать события (включая все, что было отправлено в эту очередь, включая закрытие обработчика завершения). ). Следовательно, нет тупика.

Вероятно, нет нужды говорить, что это особенность XCTestCase, но это не шаблон для использования в вашем собственном коде.

Независимо от получения дополнительной информации о том, как запускать циклы см. Руководство по программированию потоков: циклы выполнения .

0 голосов
/ 15 января 2020

Функция wait скорее всего использует NSRunLoop внутри. Выполнение l oop не блокирует основной поток, как это делают sleep функции. Несмотря на выполнение функции testX не двигается дальше. Прогон l oop по-прежнему принимает события, запланированные в потоке, и отправляет их для выполнения.

ОБНОВЛЕНИЕ:

Вот как я представляю работу прогона l oop. В псевдокоде:

while (currentDate < dateToStopRunning && someConditionIsTrue()) 
{
   if (hasEventToDispatch()) //has scheduled block?
   {
        runTheEvent();// yes, we have a block, so we run it! 
   }
}

Блок, который вы ставите для асинхронного выполнения c, проверяется внутри метода hasEventToDispatch() и выполняется. Он выполняет ожидание, которое проверяется на следующей итерации while l oop в someConditionIsTrue(), поэтому while l oop выходит. testX продолжает выполнение и after wait печатается

...