-
[iOS/Swift] Operation야매 iOS 2023. 8. 6. 22:38
Operation
작업을 클래스화 해 그 인스턴스를 사용하는 것
Opeartion은 그 자체로 사용할수도 Operation Queue에 넣어서 사용할 수도 있다.
특징
재사용성
어떤 단위적인 작업을 클래스화해서 기능을 높인 것
- 데이터 다운로드, 압축 풀기, 이미지 필터 적용
→ 단위적인 작업을 클래스화 해서 재사용성 up
단발성
클래스를 인스턴스화 했을 때 (init 했을 때) 작업은 한번만 실행가능하다
- 원하면 다시 객체를 생성해야 한다
기능
- 취소 기능
- 순서 지정(의존성)
- 작업의 실행 순서를 지정할 수 있다
- 현재의 operation이 다른 operation에 의존한다면 의존하는 operation이 모두 끝난 후에 ready 상태가 된다.
- 바로 실행되는 것이 아님, 직접 실행시켜줘야 한다.
- 끝난다고 해서 성공적으로 끝난다는 뜻이 아님. 그 성공적으로 종료된 것인지는 개발자가 직접 확인해야 한다.
- 전제되어야 하는 작업이 끝나지 않은 상태에서 실행시키면 오류 발생
- 상태 체크 (State machine)
- 준비된 상태, 실행 중인 상태. 실패한 상태, 종료된 상태 등 여러 상태를 트래킹한다.
- KVO notification
- QoS (우선순위)
- completionBlock (작업을 다 마쳤을 때의 클로저)
순서 지정
final class DelaySumOperation: Operation { var inputNum: Int var outputNum: Int = 0 init(inputNum: Int) { self.inputNum = inputNum } override func main() { print("is Operation executing \\(isExecuting)") sleep(5) outputNum = inputNum * 2 print("finished Operation") } } let firstOperation = DelaySumOperation(inputNum: 5) let secondOperation = DelaySumOperation(inputNum: 10) secondOperation.addDependency(firstOperation)
중요 메서드 및 프로퍼티
start()
오퍼레이션을 실행 시키는 메서드
- operation 내부에 정의된 main 메서드를 실행시킨다.
cancel()
오퍼레이션을 취소시키는 메서드
상태 프로퍼티
isReady
isExecuting
isCancelled
isFinished
let firstOperation = DelaySumOperation(inputNum: 5) let secondOperation = DelaySumOperation(inputNum: 10) print("is first Operation Ready? \\(firstOperation.isReady)") print("is second Operation Ready? \\(secondOperation.isReady)") print(Date()) firstOperation .start() print("is first Operation Finished? \\(firstOperation.isFinished)") print(Date()) secondOperation .start() print("is second Operation Finished? \\(secondOperation.isFinished)") print(Date()) final class DelaySumOperation: Operation { var inputNum: Int var outputNum: Int = 0 init(inputNum: Int) { self.inputNum = inputNum } override func main() { print("is Operation executing \\(isExecuting)") sleep(5) outputNum = inputNum * 2 print("finished Operation") } } /*output is first Operation Ready? true is second Operation Ready? true 2023-06-12 23:02:28 +0000 is Operation executing true finished Operation is first Operation Finished? true 2023-06-12 23:02:33 +0000 is Operation executing true finished Operation is second Operation Finished? true 2023-06-12 23:02:38 +0000 */
Life Cycle
오퍼레이션큐는 오퍼레이션의 상태를 체크하고 관리한다.
위 그림처럼 opeartion queue가 queue에 들어있는 opeartion의 상태를 체크하고 관리하는 것을 확인할 수 있다.
→ operation queue가 operation의 순서를 지정하거나 취소시키기 위해 상태 프로퍼티 사용
let firstOperation = DelaySumOperation(inputNum: 5) let secondOperation = DelaySumOperation(inputNum: 10) firstOperation.start() print("first Operation status") print("isReady \\(firstOperation.isReady)") print("isExecuting \\(firstOperation.isExecuting)") print("isCancelled \\(firstOperation.isCancelled)") print("isFinished \\(firstOperation.isFinished)") print("") firstOperation.cancel() print("first Operation status") print("isReady \\(firstOperation.isReady)") print("isExecuting \\(firstOperation.isExecuting)") print("isCancelled \\(firstOperation.isCancelled)") // 이미 실행 완료되었으므로 cancel 해도 true로 바뀌지 않음 print("isFinished \\(firstOperation.isFinished)") print("") secondOperation.cancel() print("second Operation status") print("isReady \\(secondOperation.isReady)") print("isExecuting \\(secondOperation.isExecuting)") print("isCancelled \\(secondOperation.isCancelled)") // cancel true로 변경 print("isFinished \\(secondOperation.isFinished)") print("") secondOperation.start() // main 메서드 실행시키지 않음 print("second Operation status") print("isReady \\(secondOperation.isReady)") print("isExecuting \\(secondOperation.isExecuting)") print("isCancelled \\(secondOperation.isCancelled)") print("isFinished \\(secondOperation.isFinished)") print("") print("first Operation output \\(firstOperation.outputNum)") print("second Operation output \\(secondOperation.outputNum)") /* output is Operation executing true finished Operation first Operation status isReady true isExecuting false isCancelled false isFinished true first Operation status isReady true isExecuting false isCancelled false isFinished true second Operation status isReady true isExecuting false isCancelled true isFinished false second Operation status isReady true isExecuting false isCancelled true isFinished true */
Components
Operation은 애플에서 만든 추상 클래스
개발자는 추상 클래스 상속 받아서 필요한 Operation을 만든다
input
입력 값
output
출력 값
main (override)
input을 활용해 output을 생성하는 작업 메서드
Operation Queue
Operation을 넣어서 사용할 수 있는 대기열
- 내부적으로 GCD 기반으로 구현
생성
let operationQueue: OperationQueue = { let operationQueue = OperationQueue() return operationQueue }()
Property
maxConcurrentOperationCount
let operationQueue: OperationQueue = { let operationQueue = OperationQueue() operationQueue.maxConcurrentOperationCount = 1 return operationQueue }()
maxConcurrentOperationCount로 몇 개의 스레드를 사용할 것인지 구체적으로 설정 가능
- default는 -1 (시스템이 알아서 여러 개 설정)
- 1 (serial queue)
- 1< (concurrrent queue)
final class DelaySumOperation: Operation { var inputNum: Int var outputNum: Int = 0 init(inputNum: Int) { self.inputNum = inputNum } override func main() { sleep(3) outputNum = inputNum * 2 print("output = \\(outputNum) at \\(Date())") } } let operationQueue: OperationQueue = { let operationQueue = OperationQueue() operationQueue.maxConcurrentOperationCount = 2 return operationQueue }() for i in (0..<5) { operationQueue.addOperation(DelaySumOperation(inputNum: i)) } /* output output = 2 at 2023-06-13 09:09:30 +0000 output = 0 at 2023-06-13 09:09:30 +0000 output = 4 at 2023-06-13 09:09:33 +0000 output = 6 at 2023-06-13 09:09:33 +0000 output = 8 at 2023-06-13 09:09:36 +0000 */
qualityOfService
종류
OperationQueue 자체의 QoS를 지정할 수 있다.
- 아래와 같이 5가지의 종류를 가진다
OperationQueue.qualityOfService = .userInteractive OperationQueue.qualityOfService = .userInitiated OperationQueue.qualityOfService = .default OperationQueue.qualityOfService = .utility OperationQueue.qualityOfService = .background
기본 QoS는 .background
Queue and Operation’s quality of service
operationQueue와 Operation 모두 quality of service가 지정되어 있을 때, Operation이 실제로 실행되는 스레드의 QoS는 지정한 QoS와 동일하다.
let operationQueue: OperationQueue = { let operationQueue = OperationQueue() operationQueue.qualityOfService = .background return operationQueue }() for i in (0..<5) { if i == 1 || i == 2 { let operation = DelaySumOperation(inputNum: i) operation.qualityOfService = .userInteractive operationQueue.addOperation(operation) } else { let operation = DelaySumOperation(inputNum: i) operation.qualityOfService = .background operationQueue.addOperation(operation) } } final class DelaySumOperation: Operation { var inputNum: Int var outputNum: Int = 0 init(inputNum: Int) { self.inputNum = inputNum } override func main() { sleep(3) outputNum = inputNum * 2 print(""" output = \\(outputNum) at \\(Date()) THREAD's QoS is \\(Thread.current.qualityOfService.rawValue) operation's QoS \\(qualityOfService) queue's QoS \\(operationQueue.qualityOfService) """) } } /* output = 2 at 2023-08-06 12:36:48 +0000 THREAD's QoS is 33 operation's QoS NSQualityOfService(rawValue: 33) queue's QoS NSQualityOfService(rawValue: 9) output = 4 at 2023-08-06 12:36:48 +0000 THREAD's QoS is 33 operation's QoS NSQualityOfService(rawValue: 33) queue's QoS NSQualityOfService(rawValue: 9) output = 0 at 2023-08-06 12:36:48 +0000 THREAD's QoS is 9 operation's QoS NSQualityOfService(rawValue: 9) queue's QoS NSQualityOfService(rawValue: 9) output = 8 at 2023-08-06 12:36:48 +0000 THREAD's QoS is 9 operation's QoS NSQualityOfService(rawValue: 9) queue's QoS NSQualityOfService(rawValue: 9) output = 6 at 2023-08-06 12:36:48 +0000 THREAD's QoS is 9 operation's QoS NSQualityOfService(rawValue: 9) queue's QoS NSQualityOfService(rawValue: 9) */
Operation Queue & Operation
Operation Queue에 아래의 형태로 작업을 추가할 수 있다.
- Closure
- Operation
- Operation 배열 (waitUntilFinished argument 존재)
Operation Queue는 Operation이 추가되는 즉시 스케줄링하고 동시에 실행될 오퍼레이션의 갯수를 관리한다.
Operation Queue에 Operation 할당
Operation Queue에 Operation을 추가하면 해당 Operation이 스레드에 배정되었을 때 isExecuting 상태가 된다.
- Operation 인스턴스 그 자체를 사용할 때 start 메서드를 사용하면 바로 isExecuting 상태가 되는 것과 다르다
Operation Queue에서 Operation이 실행되거나 취소되면 오퍼레이션 큐를 떠난다
<aside> 💡 Operation 인스턴스를 생성하고 반복문 안에서 Operation Queue에 해당 인스턴스를 큐에 반복해서 넣으면 에러 발생하므로 조심해야 됨
</aside>
let operationQueue = OperationQueue() operationQueue.addOperation(operation) // or operationQueue.addOperations([operation], waitUntilFinished: false) // or operationQueue.addOperation { print("this is operation queue") }
operation을 오퍼레이션 큐에 넣는 순간 비동기적으로 실행한다
- waitUntilFinished true 설정시 동기적으로 실행
let firstOperation = DelaySumOperation(inputNum: 3) let secondOperation = DelaySumOperation(inputNum: 6) operationQueue.addOperations([firstOperation, secondOperation], waitUntilFinished: true) print("is operatinoQueue Finished \\(firstOperation.isFinished && secondOperation.isFinished)") /* output output = 12 at 2023-06-13 11:53:45 +0000 THREAD's QoS is 9 operation's QoS NSQualityOfService(rawValue: -1) queue's QoS NSQualityOfService(rawValue: 9) output = 6 at 2023-06-13 11:53:45 +0000 THREAD's QoS is 9 operation's QoS NSQualityOfService(rawValue: -1) queue's QoS NSQualityOfService(rawValue: 9) is operatinoQueue Finished true */ let firstOperation = DelaySumOperation(inputNum: 3) let secondOperation = DelaySumOperation(inputNum: 6) operationQueue.addOperations([firstOperation, secondOperation], waitUntilFinished: false) print("is operatinoQueue Finished \\(firstOperation.isFinished && secondOperation.isFinished)") /* output is operatinoQueue Finished false output = 12 at 2023-06-13 11:56:49 +0000 THREAD's QoS is 9 operation's QoS NSQualityOfService(rawValue: -1) queue's QoS NSQualityOfService(rawValue: 9) output = 6 at 2023-06-13 11:56:49 +0000 THREAD's QoS is 9 operation's QoS NSQualityOfService(rawValue: -1) queue's QoS NSQualityOfService(rawValue: 9) */
기능
Operation & sync
waitUntilAllOperationsAreFinished() 메서드를 사용해 큐의 모든 작업이 끝날 때까지 대기할 수 있다.
Suspend Queue
queue를 suspend 시키면 queue가 더 이상 작업을 진행하지 않는다.
- queue의 프로퍼티인 isSuspended에 true 또는 false 값을 할당할 수 있다.
queue가 suspend 처리되면 기존에 실행되던 오퍼레이션은 계속 실행지만 새로 추가된 opertion 또는 큐에 남아 있는 operation은 isSuspended가 false 될 때까지 스케줄링 안됨
var operationQueue: OperationQueue = { let operationQueue = OperationQueue() operationQueue.maxConcurrentOperationCount = 2 operationQueue.qualityOfService = .background return operationQueue }() for i in (0..<5) { if i == 1 || i == 2 { let operation = DelaySumOperation(inputNum: i) operation.qualityOfService = .userInteractive operationQueue.addOperation(operation) } else { let operation = DelaySumOperation(inputNum: i) operation.qualityOfService = .background operationQueue.addOperation(operation) } } DispatchQueue.global().asyncAfter(deadline: .now() + .seconds(5)) { operationQueue.isSuspended = true print("operationQueue is suspended") } DispatchQueue.global().asyncAfter(deadline: .now() + .seconds(8)) { operationQueue.isSuspended = false print("operationQueue is available") } final class DelaySumOperation: Operation { var inputNum: Int var outputNum: Int = 0 init(inputNum: Int) { self.inputNum = inputNum } override func main() { sleep(3) outputNum = inputNum * 2 print(""" output = \\(outputNum) at \\(Date()) THREAD's QoS is \\(Thread.current.qualityOfService.rawValue) operation's QoS \\(qualityOfService) queue's QoS \\(operationQueue.qualityOfService) """) } } /* output output = 2 at 2023-06-13 11:41:27 +0000 THREAD's QoS is 33 operation's QoS NSQualityOfService(rawValue: 33) queue's QoS NSQualityOfService(rawValue: 9) output = 0 at 2023-06-13 11:41:27 +0000 THREAD's QoS is 9 operation's QoS NSQualityOfService(rawValue: 9) queue's QoS NSQualityOfService(rawValue: 9) operationQueue is suspended output = 4 at 2023-06-13 11:41:30 +0000 THREAD's QoS is 33 operation's QoS NSQualityOfService(rawValue: 33) queue's QoS NSQualityOfService(rawValue: 9) output = 6 at 2023-06-13 11:41:30 +0000 THREAD's QoS is 9 operation's QoS NSQualityOfService(rawValue: 9) queue's QoS NSQualityOfService(rawValue: 9) operationQueue is available output = 8 at 2023-06-13 11:41:35 +0000 // 작업은 3초 간의 간격이 있어야 하지만 suspend된 기간이 있어 5초 후에 실행되었다. THREAD's QoS is 9 operation's QoS NSQualityOfService(rawValue: 9) queue's QoS NSQualityOfService(rawValue: 9) */
Block Operation
내부에 block(클로저)을 내장하는 Operation
- Block Operation 내부에 여러 블록들이 있고 이 블록들이 여러 스레드에 분배되어 실행된다.
- 아래의 이미지에 보이듯이 하나의 Block Operation은 하나의 GCD 그룹과 유사하게 동작한다.
특징
디스패치 그룹처럼 작업이 모두 종료되었을 때 알려준다
- notify처럼 completionBlock을 통해 알려줄 수 있다.
BlockOperation 안에 여러 블록을 넣을 수 있음
각 Block은 디폴트 글로벌 큐에서 동작 (Concurrent 큐)
시리얼하게 실행할 수 있음
- 시리얼 오퍼레이션큐로 보냄
- 디펜던시(순서) 설정
기능
Operation을 상속받아 구현한 것이므로 Operation의 기능을 모두 사용할 수 있다. (기능이 더 많은 Dispatch Group)
BlockOperation은 주로 Operation을 많이 사용하는 앱에서, GCD와 같이 더 단순한 클로저를 사용하고 싶을 때 주료 사용
internal mechanism
Block Operation은 Operation를 구성하는 block들을 DispatchQueue.global()로 보낸다.
- DispatchQueue.global은 concurrent한 queue이므로 여러 스레드에서 블록을 실행시킨다.
BlockOperation 자체는 하나의 operation이므로 동기적으로 실행된다. BlockOperation.start를 하면 내부 블록이 모두 실행될 때까지 블록된다.
let blockOperation1 = BlockOperation() let blockOperation2 = BlockOperation() let blockOperation3 = BlockOperation() let blockOperation4 = BlockOperation() blockOperation1.addExecutionBlock { print("this is first") } blockOperation1.addExecutionBlock { print("this is first") } blockOperation1.addExecutionBlock { print("this is first") } blockOperation1.addExecutionBlock { print("this is first") } blockOperation2.addExecutionBlock { print("this is second") } blockOperation2.addExecutionBlock { print("this is second") } blockOperation2.addExecutionBlock { print("this is second") } blockOperation2.addExecutionBlock { print("this is second") } blockOperation3.addExecutionBlock { print("this is third") } blockOperation3.addExecutionBlock { print("this is third") } blockOperation3.addExecutionBlock { print("this is third") } blockOperation3.addExecutionBlock { print("this is third") } blockOperation4.addExecutionBlock { print("this is fourth") } blockOperation4.addExecutionBlock { print("this is fourth") } blockOperation4.addExecutionBlock { print("this is fourth") } blockOperation4.addExecutionBlock { print("this is fourth") } blockOperation1.start() blockOperation2.start() blockOperation3.start() blockOperation4.start()
Operation과 다르게 input과 output 그리고 메인 메서드를 정의할 필요가 없다.
Block Operation을 operationQueue에 넣으면 비동기적으로 실행할 수도 있다.
Operation의 순서 관리 (종속성 - Dependencies)
의존하는 작업이 끝나고 난 후 작업을 진행하는 것
의존하는 작업이 같은 스레드에서 진행되지 않는다.
의존한다면 큐에 들어온 순서대로 진행하는 건데 여러 스레드를 사용하는 것이 의미가 있는지에 대해 생각해볼 필요가 있다.
-> 작업이 하나의 작업에 의존하는 것이 두 개 이상의 작업을 의존할 수 있기 때문에 순서 관리
var operationQueue: OperationQueue = { let operationQueue = OperationQueue() operationQueue.maxConcurrentOperationCount = 1 operationQueue.qualityOfService = .background operationQueue.isSuspended = true return operationQueue }() let totalOperation = TotalOperation() for i in (1...4) { let operation = DelayIntSumOperation(inputNum: i) totalOperation.addDependency(operation) operationQueue.addOperation(operation) } operationQueue.addOperation(totalOperation) operationQueue.isSuspended = false final class TotalOperation: Operation { var input: Int? var output: Int? override func main() { sleep(3) if input == .none { let total = dependencies .compactMap { $0 as? SumDataProvider } .compactMap { $0.sum } .reduce(0, +) input = total } output = input print("output is \\(output)") } } protocol SumDataProvider { var sum: Int? { get } } final class DelayIntSumOperation: Operation { var input: Int var output: Int? init(inputNum: Int) { self.input = inputNum } override func main() { sleep(3) output = input * 2 print(Date()) print("output of delayIntSum \\(output)") } } extension DelayIntSumOperation: SumDataProvider { var sum: Int? { return output } } /* output 2023-06-14 22:42:26 +0000 output of delayIntSum Optional(2) 2023-06-14 22:42:29 +0000 output of delayIntSum Optional(4) 2023-06-14 22:42:32 +0000 output of delayIntSum Optional(6) 2023-06-14 22:42:35 +0000 output of delayIntSum Optional(8) output is Optional(20) */
하나의 작업이 다른 작업을 의존함으로 실행 순서를 지정할 수 있는 것은 Operation의 상태 관리가 가능하도록 기능을 제공하기 때문이다.
- isExecuting, isFinished, isReady와 같은 상태 추적 프로퍼티
- Operation Queue는 상태를 파악해 순서를 관리하는 코드가 내부적으로 포함되어 있다.
OpeationQueue에 추가된 순서와 디펜던시
A Operation과 B Operation이 있고 A Operation이 종료된 후 B Operation을 실행시키려고 한다.
- B Operation이 A Operation에 의존하는 상태
하지만 만약에 큐에 B Operation을 넣고 그 후 A Operation이 있다면 어떻게 진행되는가?
→ B 작업은 A 작업이 끝나기 전까지 실행할 수 없으므로 Operation Queue는 A 작업을 먼저 실행시키고 그 후 B 작업을 실행시킨다.
- B작업은 큐에서 후순위로 밀려나는 것 같다.
- code
- var operationQueue: OperationQueue = { let operationQueue = OperationQueue() operationQueue.maxConcurrentOperationCount = 1 operationQueue.qualityOfService = .background operationQueue.isSuspended = true return operationQueue }() let totalOperation = TotalOperation() for i in (1...4) { let operation = DelayIntSumOperation(inputNum: i) totalOperation.addDependency(operation) operationQueue.addOperation(operation) } operationQueue.addOperation { print("this is just temp") } operationQueue.addOperation { print("this is just temp") } operationQueue.addOperation { print("this is just temp") } operationQueue.addOperation(totalOperation) operationQueue.isSuspended = false final class TotalOperation: Operation { var input: Int? var output: Int? override func main() { sleep(3) if input == .none { let total = dependencies .compactMap { $0 as? SumDataProvider } .compactMap { $0.sum } .reduce(0, +) input = total } output = input print("output is \\(output)") } } protocol SumDataProvider { var sum: Int? { get } } final class DelayIntSumOperation: Operation { var input: Int var output: Int? init(inputNum: Int) { self.input = inputNum } override func main() { sleep(3) output = input * 2 print(Date()) print("output of delayIntSum \\(output)") } } extension DelayIntSumOperation: SumDataProvider { var sum: Int? { return output } } /* output 2023-06-14 22:59:25 +0000 output of delayIntSum Optional(2) 2023-06-14 22:59:28 +0000 output of delayIntSum Optional(4) 2023-06-14 22:59:31 +0000 output of delayIntSum Optional(6) 2023-06-14 22:59:34 +0000 output of delayIntSum Optional(8) this is just temp this is just temp this is just temp output is Optional(20) */
Operation과 프로토콜
Operation의 순서가 있다는 것은 한 Operation의 결과가 다른 Operation에게 필요하기 때문이다.
Operation은 의존하고 있는 Operation의 결과에 접근할 수 있어야 한다.
→ 프로토콜로 해결 가능
final class DelayStringSumOperation: Operation { var input: String? var output: String? override func main() { sleep(3) if input == .none, // Opeartion이 의존하고 있는 Operation에 접근해 프로토콜로 타입 캐스팅 한 후 값 추출 let provider = dependencies .filter({ $0 is SumDataProvider }) .first as? SumDataProvider { input = "\\(provider.sum)" } output = "the output is \\(input)" print(Date()) print("output of DelayStringSum \\(output)") } } protocol SumDataProvider { var sum: Int? { get } } final class DelayIntSumOperation: Operation { var input: Int var output: Int? init(inputNum: Int) { self.input = inputNum } override func main() { sleep(3) output = input * 2 print(Date()) print("output of delayIntSum \\(output)") } } extension DelayIntSumOperation: SumDataProvider { var sum: Int? { return output } }
프로토콜을 사용하지 않는다면, 결과값을 다른 Operation에 전달해주는 Operation을 생성해야 한다.
Operation Queue와 작업 취소
Operation이 Operation Queue에 들어가면 Operation Queue가 관리하므로 어떤 다른 작업도 할 수 없음이 원칙
하지만 취소는 가능하다
작업 취소
operation을 cancel 하면 isCancelled 프로퍼티가 true로 바뀐다
하지만 OperationQueue는 실행 중에 있는 Operation이 캔슬되어도 즉시 동작을 멈추지 않는다
- main 메서드와 completionBlock은 개발자 구현 영역이기 때문
→ 그러므로 개발자가 블럭 내부에서 처리해야 한다..
isCancelled
cancel 메서드를 사용하면 operation이 중지되어야 한다는 정보를 알려준다.
- 그러므로 isCancelled는 true의 상태를 갖지만 isFinished는 false 값을 갖는다.
- operation 내부에서 isCancelled 필드를 사용해 실패 시의 동작을 지정해줘야 한다.
Operation Queue와 작업 취소
Operation이 Operation Queue에 들어가면 Operation Queue가 관리하므로 어떤 다른 작업도 할 수 없음이 원칙
하지만 취소는 가능하다
작업 취소
operation을 cancel 하면 isCancelled 프로퍼티가 true로 바뀐다
하지만 OperationQueue는 실행 중에 있는 Operation이 캔슬되어도 즉시 동작을 멈추지 않는다
- main 메서드와 completionBlock은 개발자 구현 영역이기 때문
→ 그러므로 개발자가 블럭 내부에서 처리해야 한다..
isCancelled
cancel 메서드를 사용하면 operation이 중지되어야 한다는 정보를 알려준다.
- 그러므로 isCancelled는 true의 상태를 갖지만 isFinished는 false 값을 갖는다.
- operation 내부에서 isCancelled 필드를 사용해 실패 시의 동작을 지정해줘야 한다.
var operations: [Operation] = [] for i in (0...6) { let operation = DelayIntSumOperation(inputNum: i) operation.completionBlock = { print("\\(i) operation completed") } operations.append(operation) operationQueue.addOperation(operation) } sleep(3) operations .enumerated() .forEach { $0.element.cancel() print("status of operation \\($0.element.isFinished) \\($0.element.isCancelled)") } final class DelayIntSumOperation: Operation { var input: Int var output: Int? init(inputNum: Int) { self.input = inputNum } override func main() { sleep(3) if isCancelled { print("this operation has cancelled") return } output = input * 2 print(Date()) print("output of delayIntSum \\(output)") } } /* output status of operation false true status of operation false true status of operation false true status of operation false true status of operation false true status of operation false true status of operation false true this operation has cancelled 5 operation completed 1 operation completed 6 operation completed 2 operation completed 3 operation completed 0 operation completed 4 operation completed */
강의
https://www.inflearn.com/course/iOS-Concurrency-GCD-Operation'야매 iOS' 카테고리의 다른 글
[Swift] Protocol Oriented Programming (0) 2023.08.07 [iOS/Swift] @StateObject vs @ObservedObject (0) 2023.08.07 [iOS/Swift] 동시성 문제 (0) 2023.08.06 [iOS/Swift] DispatchGroup / Dispatch Work Item (0) 2023.08.06 [iOS/Swift] GCD Queue (0) 2023.08.06