티스토리 뷰

728x90
GCD Operation
- 간단한 일 

- 메소드 위주 사용하는 작업 
- 복잡한 일

- 데이터와 기능을 캡슐화한 객체 

취소 / 순서지정 / 일시정지 (상태추적)

 

Operation 

 Single-Shot-Object

  • 인스턴스화 → 작업을 한번만 실행가능하게 합니다 
  • 동일한 작업을 반복해야 하는 경우 매번 새로운 1인스턴스를 생성해야 합니다.

 기본적으로 sync(동기)로 실행합니다.

 

동일한 작업을 반복해야 하는 경우 매번 새로운 인스턴스를 생성해야 합니다.

 


Operation 의 고유기능 

  • 취소
  • 순서지정 (의존성)
  • 상태 체크 (state machine)
  • KVO notifications
  • Qos 수준
    • 우선순위 고려
  • completionBlock 제공
    • completion closure 내장

 

사용방법 

 

input, output, main() 으로 구성하면 되며, 흐름은 아래와 같습니다.

 

input - 재료

main - 작업 

output - 결과물

 

상황 input (재료) main()  (작업) output (결과물)

url로 압축 파일 다운

이미지 압축파일을 압축해제

기존 이미지를 흐릿하게 편집 

url

이미지 압축파일

이미지

데이터 다운로드

압축풀기

이미지 필터 적용하기

압축 파일

이미지

(필터된) 이미지

 

코드예시

더보기
// 기존 사진 -> 흐릿하게 변경
class TiltShiftOperation: Operation {
    var inputImage: UIImage?
    var outputImage: UIImage?
    
    override func main() {
        {
          outputImage = tiltShift(image: inputImage)
        }
    }
    
}

let inputImage = UIImage(named: "사진.jpg")

// 오퍼레이션 인스턴스화 - 한번만 실행할 수 있는 Single-shot 객체
let tsOp = TiltShiftOperation()
tsOp.inputImage = inputImage

ts.Op.start()


// 기존 사진에서 뿌옇게 처리된 사진으로 변환됨
ts.Op.outputImage

 

 

Operartion 의 메소드, 변수 

 

start()

  - OperationQueue에 넣지 않고 독립적으로 실행할 수 있게 하는 메소드

 

cancel()

  - 실행중인 operation을 취소 시키는 메소드 

  - isCancelled = true 로 변환

 

 

Operation의 4가지 상태 (LifeCycle) 

 

 

• isReady
  - Operation을 인스턴스화 (객체화) 하는 시점에 isReady = true 가 됩니다.

 

isExecuting
  - start() 메소드를 호출해 작업이 시작된 경우를 말합니다.

 

 isCancelled
  - isFinished 이전 상태 ( isReady, isExecuting )에서 변경 가능 합니다.

 

 isFinished

  - 작업이 완료된 경우 isFinished = true 로 변경되고 Queue에서 해당 Operation은 제거됩니다.

 

 

OperationQueue

 

일반적으로 Operation을 담아서 사용하는 OperationQueue (Class)

 

  1. 몇개의 Thread를 사용할건지 구체적으로 설정이 가능합니다.  
    • maxConcurrentOperationCount = -1 (기본값) (여러 Thread를 시스템이 알아서 사용)
    • maxConcurrentOperationCount = 1 ( Serial )
    • maxConcurrentOperationCount = 2 ( 2개의 Thread 사용)

  2. QoS(서비스 품질) 설정
    • 기본값은 background 로 되어 있으나 구체적으로 설정 가능 합니다.

      아래와 같이 설정
      • queue.qualityOfService = .userInteractive
      • queue.qualityOfService = .userInitiated
      • queue.qualityOfService = .default
      • queue.qualityOfService = .utility
      • queue.qualityOfService = .background

  3. Operation 추가 방법
    • 클로저, 오퍼레이션, 오퍼레이션 배열 (*waitUntilFinished 인자가 존재) 
    • 오퍼레이션을 오퍼레이션큐에 넣었을때는 Thread에 배정되었을때 isExecuting 상태가 됩니다.

      *
      waitUntilFinished - 오퍼레이션 큐에 있는 작업들이 다 끝날때 까지 기다립니다.   
  4. 오퍼레이션이 한번 실행되거나, 취소되면 오퍼레이션큐를 떠남 (사라짐)

  5. 동기적으로 기다리는 메소드 존재 waitUntilAllOperationsAreFinished() 

  6. 기능: 일시중지 - 재개 가능
    • isSuspended = true / false 직접 설정 가능
      • isSuspended = true : 기존에 실행되던 오퍼레이션은 계속 진행
      • isSuspended = false : 작업재개 

 

 

코드예시

더보기



// OperationQueue 생성
let printerQueue = OperationQueue()

printerQueue.maxConcurrentOperationCount = 3  // 3개의 작업이 비동기적으로 실행 
// printerQueue.maxConcurrentOperationCount = 1  // 1개의 작업씩 차례대로 실행 (Serial)

// '클로저' 로도 사용할 수 있으나,
printerQueue.addOperation { print("Hello,"); sleep(3) }
printerQueue.addOperation { print("Hello,"); sleep(3) }
printerQueue.addOperation { print("This"); sleep(3) }
printerQueue.addOperation { print("is"); sleep(3) }
printerQueue.addOperation { print("Operation"); sleep(3) }
printerQueue.addOperation { print("Class"); sleep(3) }

// 아래처럼 사용할 수도 있습니다.
printerQueue.addOperation(op: Operation)
printerQueue.addOperations(ops: [Operation], waitUntilFinished: Bool)

// waitUntilFinished: true 일때 
// [Operation] 이 다 끝날때 까지 기다립니다.

 

만약 오퍼레이션에서 완료하고 필요한 작업이 UI관련 작업이었다면 아래 두 가지 방법으로 사용하시면 됩니다.

DispatchQueue.main  또는 OperationQueue.main  

 

BlockOperation

다른말로 closure Operation 이라고도 하며, DispatchGroup과 유사하게 동작 합니다.

 

사용방법

var result: Int?

// BlockOperation 생성
let summationOperation = BlockOperation {
    result = 2 + 3
    sleep(3)
}

// 오퍼레이션을 상속했으니 start() 메소드도 사용가능 합니다.(내부의 block들은 concurrent하게 동작하지만, 감싸고 있는 오퍼레이션 자체는 동기적으로 동작)

summationOperation.start()


result

 

코드예시

더보기
// BlockOperation 생성
let multiPrinter = BlockOperation()

multiPrinter.completionBlock = {
    print("===모든 출력의 완료!===")
}

// addExecutionBlock에 작업들이 들어가지만 Block 내부에서 아래 5개의 명령어가 '비동기'적으로 실행 됩니다.
multiPrinter.addExecutionBlock {  print("Hello,"); sleep(2) }
multiPrinter.addExecutionBlock {  print("This"); sleep(2) }
multiPrinter.addExecutionBlock {  print("is"); sleep(2) }
multiPrinter.addExecutionBlock {  print("Operation"); sleep(2) }
multiPrinter.addExecutionBlock {  print("Class"); sleep(2) }


multiPrinter.start()

// Block 내부의 작업은 '비동기'적으로 처리 됩니다.
// Class
// This
// Operation
// is
// Hello,
// ===모든 출력의 완료!===

 

실 사용 예시

let blockOperation = BlockOperation()


for (idx, name) in someArray.enumerated() {
    blockOperation.addExecutionBlock {
        // 작업 로직
    }
}
  
blockOperation.completionBlock = {
    // blockOperation의 작업들이 다 완료되면 실행되는 코드
    
}

 

 

비동기 오퍼레이션

 

비동기 오퍼레이션을 사용하려면 아래의 코드를 사용하면 됩니다.

class AsyncOperation: Operation {
    // Enum 생성
    enum State: String {
        case ready, executing, finished
        
        // KVO notifications을 위한 keyPath설정
        fileprivate var keyPath: String {
            return "is\(rawValue.capitalized)"
        } // isReady/isExecuting/isFinished
    }
    
    // 직접 관리하기 위한 상태 변수 생성
    var state = State.ready {
        willSet {
            willChangeValue(forKey: newValue.keyPath)
            willChangeValue(forKey: state.keyPath)
        }
        didSet {
            didChangeValue(forKey: oldValue.keyPath)
            didChangeValue(forKey: state.keyPath)
        }
    }
}

extension AsyncOperation {
    // 상태속성은 모두 read-only
    override var isReady: Bool {
        return super.isReady && state == .ready
    }
    
    override var isExecuting: Bool {
        return state == .executing
    }
    
    override var isFinished: Bool {
        return state == .finished
    }
    
    override var isAsynchronous: Bool {  // 무조건 true로 리턴
        return true
    }
    
    override func start() {
        if isCancelled {
            state = .finished
            return
        }
        main()
        state = .executing
    }
    
    override func cancel() {
        super.cancel()
        state = .finished
    }
}

 

 

 

Quiz. 

Q: Operation은 기본적으로 sync, async 둘 중 어떤걸로 설정이 되어있는지?

A: sync

 

Q: Operation의 상태 4 가지?

A: isReady, isExecuting, isCancelled, isFinished

 

Q: OperationQueue의 기본 QoS는? 

A: .background

a

Q: maxConcurrentOperationCount 프로퍼티의 역할은?

A: 지정해 놓은 숫자 만큼 Operation이 동시에 몇개 까지 실행할 수 있는지 설정.

 

Q: maxConcurrentOperationCount 의 값이 1일 경우 ?

A: Serial 하게 동작 

 


참고

iOS Concurrency(동시성 프로그래밍)에 대한 이해

728x90