Operation 객체를 만들고 Operation Queue에 추가해서 작업을 추가하는 방법
하나의 작업을 나타내는 객체이다. 보통은 Operation class를 상속한 Block Operation을 사용하거나 Operation class를 subClassing한 커스텀 Operation을 구현한다.
오퍼레이션은 Single-shot Object
인데, 이는 한 번 실행한 작업을 재실행 할 수 없다는 의미이다. 똑같은 작업을 다시 실행하고 싶다면 새로운 인스턴스를 다시 만들어야 한다.
오퍼레이션은 기본적으로 동기 방식으로 실행된다. 비동기 방식을 원한다면 추가적으로 구현해야 한다. 즉, 개별적으로 실행하는 것보다는 오퍼레이션 큐에 추가하는 것이 낫다.
큐에 작업을 추가해두면 오퍼레이션 큐가 작업을 하나씩 꺼내서 실행한다.
방법은 두가지다.
1번 방법
let queue = OperationQueue.main // 메인스레드에서 작업하는 기본 큐 리턴
//오퍼레이션에 UI를 업데이트하는 기능이 있다면 사용
2번 방법-직접만들기
let queue = OperationQueue() // 백그라운드에서 실행
addOperations
는 의존성을 설정하거나 여러개를 동시에 추가 시 사용 let queue = OperationQueue() // 백그라운드에서 실행
@IBAction func startOperation(_ sender: Any) {
//클로저 형태
queue.addOperation {
for _ in 1 ... 30 {
print("😀", separator: " ", terminator: " ")
Thread.sleep(forTimeInterval: 0.3)
}
}
//블록 형태
//하나의 오퍼레이션에 두 개의 블록 추가할 수 있다는 장점
let op = BlockOperation {
for _ in 1 ... 30 {
print("😡", separator: " ", terminator: " ")
Thread.sleep(forTimeInterval: 0.6)
}
}
queue.addOperation(op)
//작업이 동시에 실행되는 것은 아니지만, 작업을 종류별로 구분하여 추가 가능-> 가독성 높아짐
//아직 실행하지 않았거나 실행중인 오퍼레이션에 블록 추가는 문제 없지만, 실행이 끝난 오퍼레이션에 추가하면 크래시가 발생한다.
op.addExecutionBlock {
for _ in 1 ... 30 {
print("🥶", separator: " ", terminator: " ")
Thread.sleep(forTimeInterval: 0.5)
}
}
}
Ready
Executing
Finished
Cancelled
4 가지 상태가 있다.
오퍼레이션을 만들면 ready,
오퍼레이션 큐에 추가하면 executing,
작업이 정상 완료되면 finished,
작업이 취소되면 cancelled 상태가 된다.
single-shot 특성상 이전 단계로 돌아갈 수는 없다.
아직 시작하지 않은 작업은 언제든지 취소 가능하다.
그러나 실행중인 작업은 취소 기능을 구현해야지만 취소 가능하다.
@IBAction func cancelOperation(_ sender: Any) {
//큐에 추가된 모든 오퍼레이션이 취소되어야 하지만, 이미 실행중인 오퍼레이션은 취소 기능이 구현되어있지 않다면 불가능하다.
queue.cancelAllOperations()
}
이 예제에서 작성한 이모지를 프린트하는 작업에서 매 반복 회차마다 오퍼레이션 상태를 확인하고, 취소되었다면 코드를 종료하도록 구현해야 한다.
하지만 addOperation
을 사용하거나 BlockOperation
을 사용할 때는 상태를 확인할 수 있는 방법이 없다.
따라서, 취소를 구현하고자 한다면 Operation
클래스를 서브클래싱해서 구현해야만 한다.
class EmojiPrintOperation: Operation{
let type: String
init(type: String) {
self.type = type
}
//중요. 필수.
override func main() {
for _ in 1 ..< 100 {
print(type, separator: " ", terminator: " ")
Thread.sleep(forTimeInterval: 0.9)
}
}
}
isCancelled
속성으로 취소 기능 구현 가능하다.
override func main() {
for _ in 1 ..< 100 {
//작업이 취소되면 isCancelled 속성에 true가 저장된다.
guard !isCancelled else {return} //return으로 인해 종료된다.
print(type, separator: " ", terminator: " ")
Thread.sleep(forTimeInterval: 0.9)
}
}
//viewController.swift
let op2 = EmojiPrintOperation(type: "🤢")
queue.addOperation(op2)
op2.completionBlock = {
print("Done")
}
취소 기능을 구현한 초록 이모지 오퍼레이션만 Completion Handler을 실행하고 종료된다.
클로저로 구현한 오퍼레이션은 isCancelled에 접근할 수 없기 때문에 해당 방법으로 구현할 수는 없다.
하지만 vc자체에 속성을 추가하고 취소했는지 안했는지 분기를 통해 동일한 패턴으로 구현 가능하다.