Mastering Concurrency in iOS - Part 5 (Operations and Operation Queue)
private func testOperations1() {
let operation: BlockOperation = .init {
print("First Test")
// as sync
sleep(3)
}
operation.start()
}
private func testOperations2() {
let operation: BlockOperation = .init()
operation.addExecutionBlock {
print("First block executed")
}
operation.addExecutionBlock {
print("Second block executed")
}
operation.addExecutionBlock {
print("Third block executed")
}
operation.start()
/*
About to begin operation
First block executed
Third block executed
Second block executed
Operation executed
*/
// as async
}
private func testOperations3() {
let operation: BlockOperation = .init()
operation.completionBlock = {
print("Execution completed")
}
operation.addExecutionBlock {
print("First block executed")
}
operation.addExecutionBlock {
print("Second block executed")
}
operation.addExecutionBlock {
print("Third block executed")
}
DispatchQueue.global().async {
operation.start()
print("Did this run main thread: \(Thread.isMainThread)")
}
/*
About to begin operation
Operation executed
First block executed
Third block executed
Second block executed
Execution completed
Did this run main thread: false
*/
// even if in global async, should be serialized following the concept of operation execution blocks.
}
private func testOperationQueues1() {
let operationQueue: OperationQueue = .init()
// operationQueue.maxConcurrentOperationCount = 1
// with this property, serialization can be done
let operation1: BlockOperation = .init()
operation1.addExecutionBlock {
print("Operation 1 being executed")
for i in 1...10 {
print(i)
}
}
operation1.completionBlock = {
print("Operation 1 executed")
// completion block timing issue
}
let operation2: BlockOperation = .init()
operation2.addExecutionBlock {
print("Operation 2 being executed")
for i in 11...20 {
print(i)
}
}
operation2.completionBlock = {
print("Operation 2 executed")
}
operation2.addDependency(operation1)
// operation2 -> operation1. i.e. operation2 should wait for operation1 to be completed
operationQueue.addOperation(operation1)
operationQueue.addOperation(operation2)
// by default -> operation queue's task should be done concurrently
}
maxConcurrentOperationCount
값을 설정함으로써 컨커런트하게 실행 가능한 오퍼레이션 개수를 제한해버리는 것. → 일반적으로는 오퍼레이션 간의 디펜던시를 통해 실행 순서를 보장private func printOneToTen() {
DispatchQueue.global().async {
for i in 1...10 {
print(i)
}
}
}
private func printElevenToTwenty() {
DispatchQueue.global().async {
for i in 11...20 {
print(i)
}
}
}
private func testOperationQueues2() {
let operationQueue: OperationQueue = .init()
let operation1: BlockOperation = .init(block: printOneToTen)
let operation2: BlockOperation = .init(block: printElevenToTwenty)
operation2.addDependency(operation1)
operationQueue.addOperation(operation1)
operationQueue.addOperation(operation2)
}
개인적으로 실행했을 때에는 언제나 디펜던시에 따라 태스크가 실행이 되었는데, 강의 영상에서는 프린트되는 숫자를 보면 컨커런트했다...
class AsyncOperation: Operation {
enum State: String {
case isReady
case isExecuting
case isFinished
}
var state: State = .isReady {
willSet(newValue) {
willChangeValue(forKey: state.rawValue)
willChangeValue(forKey: newValue.rawValue)
}
didSet {
didChangeValue(forKey: oldValue.rawValue)
didChangeValue(forKey: state.rawValue)
}
}
override var isAsynchronous: Bool { true }
override var isExecuting: Bool { state == .isExecuting }
override var isFinished: Bool {
if isCancelled && state != .isExecuting { return true }
return state == .isFinished
}
override func start() {
guard !isCancelled else {
state = .isFinished
return
}
state = .isExecuting
main()
}
override func cancel() {
state = .isFinished
}
}
willSet
, didSet
을 통해 해당 값의 변화를 관찰start()
함수를 오버라이드해서 현재 실행 중인 상태로 변경한 뒤 메인 함수를 실행class PrintNumbersOperation: AsyncOperation {
var range: Range<Int>
init(range: Range<Int>) {
self.range = range
}
override func main() {
DispatchQueue.global().async { [weak self] in
guard let self = self else { return }
for i in self.range {
print(i)
}
self.state = .isFinished
}
}
}
private func testOperationQueues3() {
let operationQueue: OperationQueue = .init()
let operation1: PrintNumbersOperation = .init(range: Range(0...25))
let operation2: PrintNumbersOperation = .init(range: Range(26...50))
operation2.addDependency(operation1)
operationQueue.addOperation(operation1)
operationQueue.addOperation(operation2)
// serialized when async, using dependency
}