티스토리 뷰

반응형

공식문서

Apple Developer Document - Operation

Operation

단일 task(작업)과 관련된 코드 및 데이터를 나타내는 추상 클래스

Declaration

class Operation: NSObject

Overview

Operation 클래스가 추상 클래스이기 때문에 직접적으로 사용하는 것이 아닌 서브 클래스나 시스템 정의 서브클래스(NSInvocationOperation, BlockOperation) 중 하나를 사용하여 사용 가능하다. 추상 클래스이지만 Operation의 기본 구현에는 작업의 안전한 실행을 조정하는 중요한 논리가 포함된다. 이러한 built-in logic이 있기 때문에 시스템 객체와 올바르게 작동하는데 필요한 glue code보다 실제 작업 구현에 집중할 수 있다.

Operation 객체는 single-shot 객체이다. 즉 한 번 실행하면 다시 실행할 수 없다. 보통 Operation을 OperationQueue에 추가하여 작업을 실행하게 된다. Operation Queue는 작업을 보조 스레드에서 직접 실행하거나 libdispatch 라이브러리를 사용하여 간접적으로 실행한다. 이러한 내용은 OperationQueue에서 자세히 알아볼 수 있다.

만약 Operation Queue를 사용하고 싶지않다면 하나의 Operation을 start() 메서드를 사용하여 직접적으로 실행할 수 있다. 이렇게 직접적으로 작업을 실행하게 되면 준비 상태에 있지 않은 작업을 시작할 수 도 있기 때문에 많은 부담이 발생한다. 준비 상태를 확인하려면 isReady 프로퍼티를 확인하면 된다.

Operation Dependencies

Dependency(종속성)은 특정 순서로 작업을 실행하는 편리한 방법이다. addDependency(_ :), removeDependency(_ :) 메서드를 사용하여 작업의 종속성을 추가하고 제거할 수 있다. 기본적으로 종속성이 있는 작업은 모든 종속된 작업이 끝나기 전에는 준비된 상태가 되지 않는다. 마지막 종속 작업이 완료되면 드디어 해당 작업이 준비가 된 상태가 되며 실행할 수 있게 된다.

NSOperation이 지원하는 종속성은 해당 작업의 종속된 작업이 성공적으로 완료된 지에 대한 여부를 구분하지 않는다. 즉 작업을 취소해도 완료된 것으로 표시가 되는데 이러한 부분은 신경 쓰지 않는다는 말이다. 이렇게 종속된 작업들이 취소가 된 경우 작업을 진행해야 하는지에 대한 여부는 개발자가 결정해야 한다. 이를 위해 오류 추적 기능을 사용해야 한다.

KVO-Compliant Properties

NSOperation 클래스는 여러 프로퍼티를 준수하는 key-value coding(KVC), key-value observing(KVO)이다. 필요에 따라 앱에서 이러한 프로퍼티들에 프로퍼티 옵저버를 추가할 수 있다. 옵저버를 추가할 수 있는 프로퍼티는 다음과 같다.

  • isCancelled (read only)
  • isAsynchronous (read only)
  • isExecuting (read only)
  • isFinished (read only)
  • isReady (read only)
  • dependencies (read only)
  • queuePriority (readable , writable)
  • completionBlock (readable , writable)

프로퍼티에 옵저버를 연결할 수 있지만 Cocoa 바인딩을 사용하여 앱의 사용자 인터페이스 요소에 바인딩해서는 안된다. 사용자 인터페이스와 관련된 코드는 일반적으로 Main 스레드에서만 실행해야 한다. 작업은 모든 스레드에서 실행될 수 있기 때문에 해당 작업과 관련된 KVO 알림이 모든 스레드에서 유사하게 발생할 수 있다.

위의 속성 중 하나에 대한 구현을 제공하는 경우 KVC, KVO를 준수해야 한다. NSOperation 객체에 추가적인 프로퍼티를 정의하는 경우 해당 프로퍼티의 KVC, KVO와 잘 호환되게 만드는 게 좋다. KVC의 경우엔 Key-Value Coding Programming Guide를 KVO의 경우엔 Key-Value Observing Programming Guide에서 더 많은 정보를 알아볼 수 있다.

Multicore Considerations

NSOperation 클래스는 자체적으로 멀티 코어를 인식한다. 따라서 객체에 대한 접근을 동기화하기 위해 추가적인 lock을 만들지 않고 여러 개의 스레드에서 NSOperation 객체의 매서드를 호출하는 것은 안전하다. 이러한 동작은 일반적으로 작업이 생성되어 모니터링 중인 스레드와 별도의 스레드에서 실행되기 때문에 필요하다.

 

NSOperation을 상속받을 땐 오버라이드 한 메서드가 여러 개의 스레드에서 호출되어도 안전해야 한다. 만약 사용자 정의 데이터에 접근하는 것과 같은 메서드를 새로 선언한다면 그러한 메서드도 스레드로부터 안전하게 만들어야 한다. 그러므로 잠재적인 데이터 손상을 방지하기 위해 모든 데이터 변수의 접근을 동기화해야 한다. 동기화에 관한 자세한 내용은 Threading Programming Guide를 참고하면 된다.

Asynchronous Versus Synchronous Operations

Operation을 Operation Queue에 넣지 않고 수동으로 실행하려는 경우 작업을 synchronous(동기식), asynchronous(비동기식)로 실행하도록 만들어야 한다. 작업은 기본적으로 동기식이다. 동기식 작업에서는 작업이 실행될 별도의 스레드를 만들지 않는다. 동기식 작업의 start() 메서드를 호출하면 현재 스레드에서 바로 작업을 실행하게 된다. 동기식 작업의 start() 메서드가 호출자에게 다시 제어를 돌려주면 작업은 완료된 상태가 된다.

 

만약 start() 메서드를 비동기식 작업에서 호출하게 되면 해당 작업이 완료되기 전에 해당 메서드가 반환될 수 있다. 비동기 작업은 별도의 스레드에서 작업을 스케줄링한다. 작업은 새로운 스레드에서 바로 시작될 수 있고 비동기 메서드를 호출하거나 dispatch queue에 블록을 제출하여 실행할 수 있다. 제어가 다시 호출자에게 반환될 때 작업이 진행 중인지는 중요하지 않다.

 

만약 queue를 사용하여 작업을 계획한다면 비동기식으로 정의하는 것이 간단하다. 만약 작업을 수동으로 실행한다면 비동기로 정의할 수 있다. 비동기식으로 정의하는 것은 어렵다. 그 이유는 작업들이 KVO를 잘 준수하는지에 대한 지속적인 관찰이 필요하기 때문이다. 하지만 수동으로 실행된 작업이 호출 스레드를 차단하지 않으려면 비동기식 작업으로 정의하는 것이 좋다.

 

Operation Queue에 작업을 추가할 때 queue는 isAsynchronous 프로퍼티의 값을 무시하고 항상 다른 스레드에서 start() 메서드를 호출한다. 그러므로 만약 항상 operation queue를 사용하여 작업을 실행할 것이라면 비동기로 만들 필요가 없는 것이다.

Subclassing Notes

NSOperation 클래스는 작업의 실행 상태를 추적할 수 있는 기본 논리를 제공하지만 실제 작업을 수행하려면 상속받은 클래스를 만들어야 한다. 상속받은 클래스를 작성하는 방법은 동기식인지 비동기식인지에 따라 다르다.

Methods to Override

비동시성 작업에서는 하나의 메서드만 오버라이드 하면 된다

이 메서드에 작업에 필요한 동작들을 작성하면 된다. 인스턴스 생성이 쉽도록 사용자 정의 생성자도 만들 수 있다. 작업의 데이터에 접근하기 위해 getter, setter 메서드를 정의할 수도 있다. 하지만 만약 getter, setter를 정의하는 경우엔 여러 개의 스레드에서 해당 메서드를 안전하게 호출할 수 있는지 확인해야 한다.

동시성 작업을 만들 경우엔 최소한 다음의 4개의 메서드와 프로퍼티는 오버라이드 해야 한다.

동시성 작업에서는 start() 메서드가 비동기식 작업을 시작한다. 스레드를 생성하든 비동기식 함수를 호출하든 이 메서드 안에서 수행된다. 작업을 시작하면 start() 메서드는 isExecuting 프로퍼티의 보고에 따른 작업의 실행 상태를 업데이트해야 한다. isExecuting 키 경로에 관심이 있는 클라이언트에게 KVO 알림을 보내 현재 작업이 실행 중임을 알릴 수 있다. isExecuting 프로퍼티도 스레드 안전 방식으로 상태를 알려줘야 한다.

 

작업이 완료되거나 취소되면 동시성 작업은 isExecuting, isFinished 키 경로에 대해 KVO 알림을 생성하여 작업의 최종 상태를 변경해줘야 한다. (취소의 경우 작업이 완료되지 않았더라도 isFinished를 업데이트하는 것이 중요하다. Queue에서는 이러한 작업이 완료된 것으로 보고하게 된다.) 오버라이드 한 isExecuting, isFinished 프로퍼티도 KVO 알림을 발생시키기 위해 작업 상태에 따라 정확한 값을 계속 보고해야 한다.

 

동시성 작업을 정의하는 좀 더 자세한 방법은 Concurrency Programming Guide를 참고하면 된다.

여기서 중요한 것은 start() 메서드에서 절대 super를 호출하면 안 된다는 점이다. 동시성 작업을 정의할 때 default start() 메서드와 동일한 동작을 제공해야 한다. start() 메서드는 실제로 작업이 취소되었는지에 대한 여부를 작업 실행 전에 확인해야 한다. 작업 취소에 대한 정보는 Responding to the Cancel Command에서 더 알아볼 수 있다.

 

동시성 작업의 경우에도 위에서 언급한 네 가지의 메서드와 프로퍼티를 제외하고는 더 오버라이드 할 건 없다. 작업의 종속성 기능을 다룰 경우 추가적인 메서드를 오버라이드하고 KVO 알림을 제공해야 할 수도 있다. 종속성의 경우 isReady 키 경로에 대한 알림만 제공하면 된다. 종속성 프로퍼티에는 종속된 작업 목록이 포함되어 있기 때문에 발생하는 변경사항은 default NSOPeration 클래스에서 처리하게 된다.

Maintaining Operation Object States

작업은 내부적으로 작업 상태 정보를 유지하여 실행이 안전할 때를 결정하고 작업 수명주기를 통해 진행 상황을 외부에 알린다. 상속받은 클래스들은 이러한 상태 정보로 올바른 실행을 하게 된다. 작업 상태와 관련된 주요 경로는 다음과 같다.

  • isReady
    isReady 키 경로는 작업이 실행 가능하다는 것을 클라이언트들이 알 수 있게 해 준다. 만약 작업이 현재 실행 가능하다면 true를 실행 가능하지 않다면 false를 저장하고 있다. 대부분의 경우 개발자가 isReady를 직접 수정할 필요는 없다. 하지만 작업의 준비가 종속된 작업 이외의 요인에 의해 결정되는 경우 isReady 프로퍼티를 직접 구현하고 추적해야 한다. 맥 OS 10.6부터 하나이상의 종속된 작업이 기다리는 작업을 취소할 경우 해당 종속성은 무시되고 isReady 프로퍼티는 true로 업데이트된다. 이러한 동작은 operation queue에서 취소된 작업을 더 빨리 플러시 할 수 있는 기회를 제공한다.
  • isExecuting
    isExecuting 키 경로는 작업이 할당된 작업을 수행 중인지에 대한 여부를 클라이언트에게 알려준다. 작업이 수행 중인 경우엔 true, 그렇지 않으면 false를 저장하고 있다. 작업의 start() 메서드를 바꾸면 isExecuting 프로퍼티도 바꾸고 작업의 실행상태가 변경될 때 KVO 알림을 생성해야 한다.
  • isFinished
    isFinished 키 경로는 작업이 성공적으로 완료되었거나 실행 중 취소되었을 때 클라이언트들이 알 수 있게 해 준다. isFinished의 값이 true로 변경될 때까지 작업은 종속석을 지우지 않는다. 비슷하게 operation queue의 경우에도 작업을 큐에서 제외시키지 않는다. 즉 이 프로퍼티를 true로 변경하는 것은 진행 중인 작업이나 취소된 작업으로 큐에 백업되지 않도록 하는데 중요하다. start() 메서드를 바꾸면 isFinished 프로퍼티도 바꾸고 작업의 실행상태가 변경될 때 KVO 알림을 생성해야 한다.
  • isCancelled
    isCancelled 키 경로는 작업이 클라이언트에게 작업 취소가 요청되었음을 알려준다. 취소에 대한 지원은 선택사항이며 KVO 알림을 보내지 않아도 된다.

Responding to the Cancel Command

한번 queue에 작업이 추가되면 그 작업은 더 이상 개발자가 어떻게 할 수 없다. queue는 해당 작업을 스케줄링하여 처리한다. 하지만 그러한 작업을 추가한 뒤에 실행하고 싶지 않다면 작업을 취소하여 CPU 시간을 소비하지 않도록 할 수 있다. 이러한 동작은 cancel() 메서드를 사용하면 되고 작업 자체를 중단하고 싶다면 OperationQueue 클래스의 cancelAllOperations() 메서드를 호출하면 된다.

 

작업을 취소한다고 해서 즉시 작업을 중단하는 것은 아니다. isCancelled 프로퍼티가 모든 작업에서 알아서 수정될 것이라고 예상되지만 실제론 명시적으로 확인하고 필요에 따라 중단해야 한다. NSOperation의 기본 구현에는 취소를 했는지에 대한 확인이 포함된다. 예를 들어 start() 메서드가 호출되기 전에 해당 작업을 취소하면 start()메서드가 작업을 시작하지 않고 종료된다.

 

맥 OS 10.6 이상에서 operation queue에 있는 작업 중 종속되어있는 작업들이 끝나지 않은 상태에서 cancel() 메서드로 취소하면 종속된 작업들은 무시된다. 이유는 작업은 이미 취소되었기 때문에 이러한 행동은 작업의 start() 메서드가 main() 메서드를 호출하지 않고 작업을 제거할 수 있다. 만약 큐 안에 있지도 않은 작업에 대해 cancel() 메서드를 호출하면 해당 작업은 즉시 취소된 것으로 표시된다. 각 경우에 준비 완료 또는 완료로 표시하면 적절한 KVO 알림이 생성된다.

 

작성하는 모든 코드에서 항상 cancellation semantics를 지원해야 한다. 특히 main task code는 isCancelled 프로퍼티의 값을 주기적으로 확인해야 한다. 프로 퍼트의 값이 true가 된다면 작업을 최대한 빨리 정리하고 종료해야 한다. 만약 사용자 정의 start() 메서드를 사용한다면 취소에 대한 조기 점검을 포함해야 하고 적절히 대응해야 한다. 사용자 정의 start() 메서드는 조기 취소를 처리할 수 있도록 정의되어야 한다.

작업이 취소될 때 취소된 작업을 적절한 최종 상태로 이동해야 한다. 특히 isFinished, isExecuting 프로퍼티를 개발자가 직접 관리한다면 취소가 발생 시 이러한 프로퍼티의 값도 수정해줘야 한다. isFinished의 값은 true로 isExecuting의 값은 false로 업데이트해줘야 한다. 이러한 작업은 작업이 실행 전에 취소되었더라도 수행해줘야 한다.

Topics

Executing the Operation

func start()
// 작업의 실행을 시작한다.

func main()
// non-concurrent작업을 실행한다.

func completionBlock: (() -> Void)?
// 작업의 main task를 수행 후에 해당 블럭을 실행한다.

Canceling Operations

func cancel()
// 실행을 중지하라고 작업에게 알린다.

Getting the Operation Status

var isCancelled: Bool
// 작업이 취소되었는지에 대한 여부

var isExecuting: Bool
// 작업이 실행중인지에 대한 여부

var isFinished: Bool
// 작업이 완료되었는지에 대한 여부

var isConcurrent: Bool
// 작업이 비동기식으로 실행되는지에 대한 여부

var isAsynchronous: Bool
// 작업이 비동기식으로 실행되는지에 대한 여부

var isReady: Bool
// 작업이 실행할 수 있는 준비가 되었는지에 대한 여부

var name: String?
// 작업의 이름

Managing Dependencies

func addDependency(Operation)
// 특정 작업을 종속성에 추가하는 함수

func removeDependency(Operation)
// 특정 작업을 종속성에서 제거하는 함수

var dependencies: [Operation]
// 현재 작업의 실행 준비가 완료되기 전에 실행을 완료해야하는 작업들의 배열

Configuring the Execution Priority

var queuePriority: Operation.QueuePriority
// Operation queue에서의 실행 우선순위

Waiting on an Operation Object

func waitUntilFinished()
// 작업이 완료될 때까지 현재 스레드의 실행을 차단

Constants

enum Operation.QueuePriority
// 작업이 실행되는 순서의 우선순위를 지정할 수 있다

enum QualityOfService
// 서비스 작업의 본질과 중요성을 나타내는 데 사용. 더 높은 서비스 클래스를 사용하는 작업은 더 많은 리소스를 받을 수 있다.
반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함