Как отметил Андреас, документация предупреждает нас :
Блоки, добавленные к операции блока, отправляются с приоритетом по умолчанию для соответствующей рабочей очереди. Сами блоки не должны делать никаких предположений относительно конфигурации среды их выполнения.
Поток, в котором мы start
выполняем операцию, а также поведение maxConcurrentOperationCount
очереди, управляется на уровне операции, а не на отдельных блоках выполнения в операции. Добавление блока в существующую операцию - это не то же самое, что добавление новой операции в очередь. Очередь операций управляет взаимоотношениями между операциями, а не между блоками внутри операции.
Проблему можно обнажить, заставив эти блоки выполнять что-то, что занимает немного времени. Рассмотрим задачу, которая ждет одну секунду (как правило, вы бы никогда не сделали sleep
, но мы делаем это просто для того, чтобы смоделировать медленную задачу и продемонстрировать поведение, о котором идет речь). Я также добавил необходимый код «точки интереса», чтобы мы могли наблюдать это в инструментах, что упрощает визуализацию происходящего:
import os.log
let pointsOfInterest = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: .pointsOfInterest)
func someTask(_ message: String) {
let id = OSSignpostID(log: pointsOfInterest)
os_signpost(.begin, log: pointsOfInterest, name: "Block", signpostID: id, "Starting %{public}@", message)
Thread.sleep(forTimeInterval: 1)
os_signpost(.end, log: pointsOfInterest, name: "Block", signpostID: id, "Finishing %{public}@", message)
}
Затем используйте addExecutionBlock
:
let queue = OperationQueue() // you get same behavior if you replace these two lines with `let queue = OperationQueue.main`
queue.maxConcurrentOperationCount = 1
let operation = BlockOperation {
self.someTask("main block")
}
operation.addExecutionBlock {
self.someTask("add block 1")
}
operation.addExecutionBlock {
self.someTask("add block 2")
}
queue.addOperation(operation)
Теперь я добавляю это в очередь последовательных операций (потому что вы никогда не добавите блокирующую операцию в основную очередь ... нам нужно, чтобы эта очередь была свободной и отзывчивой), но вы видите то же самое поведение, если вы вручную start
это на OperationQueue.main
. Таким образом, нижняя строка, в то время как start
будет запускать операцию «немедленно в текущем потоке», любые блоки, добавленные с помощью addExecutionBlock
, будут просто параллельно выполняться в «соответствующей рабочей очереди», необязательно в текущем потоке.
Если мы посмотрим это в Instruments, мы увидим, что не только addExecutionBlock
не обязательно учитывает поток, в котором была запущена операция, но и не учитывает последовательный характер очереди, с параллельными блоками:
Очевидно, что если вы добавите эти блоки как отдельные операции, то все в порядке:
for i in 1 ... 3 {
let operation = BlockOperation {
self.someTask("main block\(i)")
}
queue.addOperation(operation)
}
Выход: