Notice
Recent Posts
Recent Comments
Link
관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] Operation, OperationQueue, 동시성 본문

iOS 응용 (swift)

[iOS - swift] Operation, OperationQueue, 동시성

jake-kim 2021. 12. 12. 23:54

Operation

  • single task에 관한 데이터와 코드를 나타내는 추상 클래스
  • 해당 클래스를 서브클래싱하여 사용하면 안정적으로 task를 실행시킬 수 있는 효과 존재

OperationQueue

  • Operation 객체들을 priority에 의해서 실행시키는 queue
  • 한번 operation queue에 Opertaion 객체를 담아놓으면 task가 끝날때까지 queue가 없어지지 않고 존재
  • 해당 queue에서 Operation들을 삭제하거나 명령시킬 수 있는 장점이 존재 (task관리에 이점)

Operation, OperationQueue의 핵심

  • Operation을 서브클래싱한 것은 기능을 캡슐화 했다는 의미이므로, 기능에 관한 모듈성 향상
  • task의 실행, 정지, 대기와 같은 실행 상태를 알 수 있고 이를 통해 Operation들을 취소, 순서 지정이 가능
  • 진행 상황과 종속성을 추적하면서 여러 클래스에 대한 책임을 분리할 수 있는 방법

Operation State

  • Operation에는 내부적으로 상태값들이 존재하고 상태를 읽어서 특정 처리를 할 수 있는 장점이 존재
    • pending: Queue에 Operation(task)가 추가 될 경우
    • Ready: Pending에서 모든 조건 만족 시 해당 상태로 변경
    • Executing: start() 메소드를 호출하여 작업이 시작된 경우
    • Finished: 작업이 완료된 경우 해당 상태로 변경되고 Queue에서 Operation이 제거
    • Cancelled: 해당 상태는 3개의 상태 Pending, Ready, Executing에서만 변경 가능하고, Cancelled 상태가 되었다가 곧바로 Finished 상태로 변경

GCD vs Operation

  • Operation, Operation Queue는 내부적으로 GCD를 사용 
  • GCD는 동시에 실행될 작업 단위를 나타내는 방법이고 종속성 (A다음에 B가 꼭 실행되게끔 설정) 설정이 어려움
  • Operation은 GCD에 비해 단순하게 사용하지 않지만, 작업 간에 종속성 추가, 재사용, 취소, 일시 중지가 가능

Operation 사용 방법

  • Operation 클래스를 subclassing하여 사용하고, main() 메소드를 overriding 해서 사용할 것
  • main(): setup 용도의 함수이며 override해서 사용 (가장 먼저 실행)
  • start()
    • 스레드를 생성하든 비동기 함수를 호출하든 이 메소드에서 수행
    • 이 메소드 사용 시, 클라이언트가 현재 실행 중임을 알 수 있도록 KeyPath 경로에 대한 KVO 알림을 전달하여 이를 수행
    • super() 호출 금지
  • isAsynchronous
  • isExecuting
  • isFinished

Operation 단순 사용법 - BlockOperation을 이용

let blockOperation = BlockOperation {
    print("Executing!")
}

let queue = OperationQueue()
queue.addOperation(blockOperation)

추상 클래스 Operation을 구현해서 모듈화

  • Operation을 상속받아서 구현 
final class ContentImportOperation: Operation {

    let itemProvider: NSItemProvider

    init(itemProvider: NSItemProvider) {
        self.itemProvider = itemProvider
        super.init()
    }

    override func main() {
        guard !isCancelled else { return }
        print("(main) Importing content...")
        
        // .. import the content using the item provider

    }
}
  • addOperation 선언 시 자동으로 실행
    • 주의: task는 한 번만 실행될 수 있으므로 완료 or 취소 상태일때는 더이상 동일한 인스턴스를 다시 시작 불가
let queue = OperationQueue()

let fileURL = URL(fileURLWithPath: "..")
let contentImportOperation = ContentImportOperation(itemProvider: NSItemProvider(contentsOf: fileURL)!)

contentImportOperation.completionBlock = {
    print("Importing completed!")
}

queue.addOperation(contentImportOperation)

// (main) Importing content...
// Importing completed!

Dependency를 이용한 작업 구현

  • B작업은 A작업이 끝난 이후에만 실행되도록 하는 방법?
    • B.addDependency(A)로 사용
let fileURL = URL(fileURLWithPath: "..")
let contentImportOperation = ContentImportOperation(itemProvider: NSItemProvider(contentsOf: fileURL)!)
contentImportOperation.completionBlock = {
    print("Importing completed!")
}

let contentUploadOperation = ContentUploadOperation(itemProvider: NSItemProvider(contentsOf: fileURL)!)
contentUploadOperation.addDependency(contentImportOperation)
contentUploadOperation.completionBlock = {
    print("Uploading completed!")
}

queue.addOperations([contentImportOperation, contentUploadOperation], waitUntilFinished: true)

// (main) Importing content...
// (main) Uploading content...
// Importing completed!
// Uploading completed!

* 전체 소스 코드: https://github.com/JK0369/ExOperation

 

* 참고

- https://www.avanderlee.com/swift/operations/

- https://www.raywenderlich.com/5293-operation-and-operationqueue-tutorial-in-swift

https://developer.apple.com/documentation/foundation/operation

- https://developer.apple.com/documentation/foundation/operationqueue

Comments