티스토리 뷰

반응형

안녕하세요 :) Zedd입니다.

흠..저는 Operation Queue는 한번도 사용해본적이 없긴 한데요..



Operation Queues


Cocoa operations은 비동기적으로 수행하려는 작업(work)을 캡슐화하는 객체지향적인 방법(object-oriented way)입니다. 

Operations은 operation queue과 함께 사용되거나 자체적으로 사용되도록 설계되었습니다.

Objective-C 기반이기 때문에 Operations은 OS X및 iOS의 Cocoa 기반 앱에서 가장 일반적으로 사용됩니다.

이 챕터에서는 Operations을 정의하고 사용하는 방법을 보여줍니다. 

시작해봅시다 XD


About Operation Objects


operation객체는 앱에서 수행 할 작업을 캡슐화하는데 사용하는 Foundation프레임워크의 NSOperation클래스의 인스턴스입니다.

NSOperation클래스 자체는 유용한 작업을 수행하기 위해서 서브클래싱 되어야 하는 추상 기본 클래스입니다. (<Concurrency Programming Guide - Concurrency and Application Design>글에서 다 했던말을 하네요.) 추상적임에도 불구하고 이 클래스는 자신의 하위클래서 해야하는 작업량을 최소화하기 위해 상당한 양의 인프라를 제공합니다. 또한 Foundation프레임워크는 기존 코드와 함께 그대로 사용 할 수 있는 두개의 concrete하위 클래스를 제공합니다. 


NSInvocationOperation : 앱의 객체 및 selector를 기반으로, operation객체를 만드는데 사용하는 클래스입니다. 이미 필요한 task를 구행하는 기존 메소드가 있는 경우, 이 클래스를 사용 할 수 있습니다. 하위클래스화가 필요하지 않으므로 이 클래스를 사용하여 보다 동적인 방식으로 Operation객체를 만들 수도 있습니다. 

==> Xcode 6.1부터 NSInvocationOperation은..Swift에서 disabled...되었기 때문에...그냥..이런게..있었다...만 알면 될 것 같아요..! 아니 그럼 공식문서에도 좀 표시를 해야하는거 아닌가..!!!! 




NSBlockOperation : 현재 하나 이상의 블록 객체를 동시에 실행하는데 사용하는 클래스입니다. 둘 이상의 블록을 실행할 수 있으므로 블록 operation객체는 그룹 semantic을 사용하여 작동합니다. 연관된 모든 블럭이 실행을 완료한 경우에만 operation자체가 완료된 것으로 간주됩니다.


NSOperation : 커스텀 Operation객체를 정의하기 위한 base class입니다. NSOperation을 하위 클래스화 하면, operation이 실행되는 기본방식을 변경하고, 상태를 보고하는 기능을 포함하여, 자체 operation의 구현을 완벽하게 제어(complete control)할 수 있습니다. (오...)


모든 operation 객체는 다음과 같은 주요 기능을 지원합니다.


● operation객체간의 그래프 기반 종속성 설정 지원. 이러한 종속성은 종속 된 모든 작업이 실행을 완료할 때까지 주어진 operation이 실행되는 것을 방지합니다.

● operation의 main task가 완료된 후에 실행되는 optional completion블록을 지원합니다.(OS X v10.6이상에만 해당)

● KVO알림을 사용하여 operation의 실행 상태 변경 모니터링을 지원합니다.

● operation 우선 순위 지정을 지원하므로, 상대적인 실행 순서에 영향을 줍니다. 

● 실행 중 조작을 정지 할 수 있는 canceling semantics기능을 지원합니다.


operation은 앱의 동시성 수준을 향상시킬 수 있도록 설계되었습니다. operation은 앱의 동작을 단순한 개별 청크로 구성하고, 캡슐화하는 좋은 방법이기도 합니다. 앱의 main쓰레드에서 코드를 실행하는 대신 하나 이상의 operation객체를 큐에 제출하고, 하나 이상의 개별 쓰레드에서 해당 작업을 비동기적으로 수행 할 수 있습니다.


Concurrent Versus Non-concurrent Operations

일반적으로 operation을 operation queue에 추가하여 operation을 실행하지만, 그렇게 하지 않아도 됩니다.

또한 start메소드를 호출하여 operation객체를 수동으로 실행 할 수도 있습니다. 그렇지만, 그렇게 해도 코드의 나머지 부분과 동시에 operation이 실행되는 것은 아닙니다. NSOperation클래스의 isConcurrent메소드는 start메소드가 호출 된 쓰레드와 관련하여 operation이 동기적/비동기적으로 실행되는지 여부를 알려줍니다. 기본적으로 이 메소드는 False(NO라고 써져있는데..Objc기준이라 이렇게 써있는거겠죠?)를 반환합니다. 이는 호출 쓰레드에서 작업이 동기적으로 실행됨을 의미합니다.

(그러니까 isConcurrent는 비동기적으로 실행되는 경우에는 true를, 동기적으로 실행되는 경우에는 false를 리턴합니다. )


궁금증 1 : 아니 저는 Concurrent라는 그런 개념과 asynchronously이런 개념이 조금 다르다고 알고있었는데...뭔가 Concurrent는 여러 task를 같은 시간에 처리한다..뭐 이런 느낌이고 asynchronously는 task를 돌려놓고 나는 다른 작업을 할 수 있는? 그냥 뭔가 둘은 느낌은 비슷하지만 다른 개념이라고 이해하고 있는데... isConcurrent가 asynchronously로 실행되면 true고 synchronously로 실행되면 false라고 하니까 뭔가 이름과...매치가 안되는...그래서 계속 헷갈리네요. 지금 밑에 쓰고 있다가 헷갈려서 계속 다시 읽어보는중...그럼 차라리 isAsynchronous라고 하던가..해서 찾아봤더니 또 isAsynchronous는 따로 있음...근데 정의는..

정의는 1도 안틀리고 같음...그럼 문서에서는 지금  isConcurrent로 말하고 있지만 isAsynchronous로 이해해도 되는 것인가?

이 문서의 revision history가 2012년이 마지막이었던 것을 알고 읽기 시작했는데...괜히 읽었나..차라리 <Operation>를 읽을 걸 그랬나 싶기도 하고 참..ㅠㅠㅠ

그렇다면 정의가 똑같으니 이제 isConcurrent와 isAsynchronous는 똑같은걸로 하고..딱 정하고 글을 쓰겠스비당!!! 같다!!!!


헉...

isConcurrent가보면 isConcurrent대신에 isAsynchronous를 사용하라고 나오네요!!!!!!!!!!!!!!!!!!!!

ㅇㅎㅇㅎㅇㅎ이런걸 써놓으란 말이야............그렇다면 이제 확실하게 말할 수 있겠네요. 


isAsynchronous를 쓰자 XD




concurrent operation(즉, 호출 쓰레드와 비동기적으로 실행되는 작업)을 구현하려면, 비동기적으로 operation을 시작하는 추가코드를 작성해야합니다. 예를들어, 별도의 쓰레드를 생성하거나, 비동기 시스템 함수를 호출하거나, start메소드가 operation을 시작하고, operation이 완료되기전에 즉각적으로 반환되도록 할 수 있습니다.


대부분의 개발자는 concurrent operation객체를 구현할 필요가 없습니다. operation을 항상 operation queue에 추가하는 경우, concurrent operation을 구현할 필요가 없습니다. nonconcurrent operation을 operation queue에 제출하면, 해당 operation을 실행할 쓰레드가 큐 자체에서 생성됩니다. 따라서 nonconcurrent operation을 operation queue에 추가해도, operation객체 코드는 비동기적으로 실행됩니다. concurrent operations을 정의하는 기능은, 해당 operation을 operation queue에 추가하지 않고, 비동기식으로 실행해야 하는 경우에만 필요합니다.

(참 이게 무슨말인가 싶은데, 첫줄에 "대부분의 개발자는 concurrent operation객체를 구현할 필요가 없습니다.”라고 그랬죠? operation을 그냥 operation queue에 제출하기만 하면 concurrent operation객체를 만든거나 다름이 없으니까요. 그러니까 굳이~~정말 굳~~~~~~이 니가 concurrent operation을 내가 만들거다! 하면, 그 만든 concurrent operation을 operation queue에 추가하지 않고 비동기적으로 실행해야 하는 경우에만 만들라는 것이죠(마지막줄) 잘 이해가 안돼서 한참을 읽었네요zzz)


Creating an NSInvocationOperation Objec

Swift에서 사용못하니 건너뛰겠


Creating an NSBlockOperation Objec

NSBlockOperation클래스는 위에서 "현재 하나 이상의 블록 객체를 동시에 실행하는데 사용하는 클래스입니다”라고 그랬죠? 한번 봅시다. 만드는 법을...예제 코드를 넣어야 한다면 Swift로 넣을거임

NSBlockOperation클래스는 하나 이상의 블록 객체에 대한 wrapper역할을 하는 NSOperation의 concrete하위 클래스입니다. 이 클래스는 이미 operation queue를 사용중이며, dispatch queues를 만들지 않으려는 앱에 객체지향 wrapper를 제공합니다. 블록 operation을 사용하여 operation종속성, KVO notifications 및 dispatch queues에서 사용 할 수 없는 기타 기능을 활용할 수도 있습니다


블록 operation을 생성할 때는 일반적으로 초기화 시간에 적어도 하나의 블록을 추가합니다. 나중에 필요에 따라 더 많은 블록을 추가할 수 있습니다.

NSBlockOperation객체를 실행할 시간이 되면, 객체는 모든 블록을 기본 우선 순위(default-priority)의 concurrent dispatch queue에 보냅ㄴ디ㅏ. 그런 다음 모든 블록이 실행될 때 까지 기다립니다. 마지막 블록의 실행이 끝나면, operation객체는 완료된 것으로 표시됩니다. 따라서 쓰레드 조인(thread join)을 사용하여 여러 쓰레드의 결과를 병합하는 것처럼 블록 실행을 사용하여, 실행중인 블록그룹을 추적(track)할 수도 있습니다. 차이점은 블록 operation자체가 별도의 쓰레드에서 실행되므로, 앱의 다른 쓰레드가 블록 operation이 완료될 때까지 다른 작업을 계속 할 수 있다는 것입니다


블록 연산 객체를 만든 후에는 addExecutionBlock메소드를 사용하여 블록 연산 객체를 더 추가할 수 있습니다. 블록을 순차적으로 실행해야하는 경우, 원하는 dispatch queue에 직접 블록을 제출해야합니다.


Defining a Custom Operation Object

블록 operation 및 invocation operation 객체가 앱의 요구 사항을 충족시키지 못하면 NSOperation을 직접 서브클래싱하여 필요한  모든 동작을 추가할 수 있습니다. NSOperation클래스는 모든 operation객체에 대한 일반적인 서브클래싱 포인트를 제공합니다. 또한 클래스는 종속성 및 KVO notifications에 필요한 대부분의 작업을 처리 할 수 있는 상당한 양의 인프라를 제공합니다. 그러나 operation이 올바르게 작동하도록 기존 인프라를 보완해야 할 때가 있습니다. 수행해야 할 추가 operation의 양은 nonconcurrent operation 또는 concurrent operation을 구현하는지 여부에 따라 다릅니다. 


nonconcurrent operation을 정의하는 것은 concurrent operation을 정의하는 것보다 훨씬 간단합니다. nonconcurrent operation의 경우, main task를 수행하고, 취소 이벤트에 적절히 응답해야합니다. 기존 클래스 인프라는 다른 모든 작업을 수행합니다.

concurrent  operation의 경우, 기존 인프라의 일부를 사용자 지정 코드로 바꿔야합니다. 다른 섹션에서는 두가지 타입의 객체를 모두 구현하는 방법을 보여줍니다. 

(concurrent  operation의 경우, 추가 코드를 작성해야줘야 하니까, nonconcurrent operation이 더 간단하다고 말한거겠죠?)


Performing the Main Task

모든 opration객체는 최소한 다음 메소드를 구현해야합니다.


● custom initialization method(커스텀 초기화 메소드)

● main


커스텀 초기화 메소드를 사용하여 operation객체를 known state(알려진 상태..?)로 두고 커스텀 main 메소드를 사용하여 task를 수행해야합니다.

다음과 같이 필요에 따라 추가 메소드를 구현할 수 있습니다.


  • main메소드의 구현으로 부터 호출하는 커스텀 메소드(...) 
  • 데이터 값을 설정하고, operation의 결과에 액세스하기 위한 접근자 메소드(Accessor method)
  • NSCoding프로토콜을 사용하여 operation객체를 archive하고 unarchive 할 수 있습니다.

Responding to Cancellation Events

operation이 실행되기 시작하면, task가 완료될 때 까지 또는 코드에서 operation을 명시적으로 취소 할 때 까지 task를 계속 수행합니다. 취소는 언제든지, 심지어 operation이 실행되기 전에 발생할 수 있습니다. NSOperation클래스는 클라이언트가 operation을 취소 할 수 있는 방법을 제공하지만, 취소 이벤트를 인식하는 것은 필요에 따라 자발적(voluntary)입니다. operation이 완전히 종료된 경우, 할당된 자원을 회수하는 방법이 없을 수 있습니다. 따라서, operation객체는 operation중간에 발생하는 취소 이벤트를 확인하고 정상적으로 종료되어야 합니다.


operation객체에서 취소를 지원하려면, 사용자 정의 코드에서 객체의 isCancelled메소드를 주기적으로 호출하고, true를 반환하면, 즉시 리턴해야합니다. 취소 지원은 operation 기간이나 NSOperation을 직접 서브클래싱하는지 또는 해당 하위 클래스 중 하나를 사용하는지에 관계없이 중요합니다. isCancelled메소드 자체는 매우 가볍고 성능상의 불이익을 주지 않고, 자주 호출 할 수 있습니다. operation객체를 디자인 할 때는 코드의 다음 위치에서 isCancelled메소드를 호출 하는 것이 좋습니다.

  • 실제 작업을 수행하기 직전.
  • 루프를 반복 할 때 마다 적어도 한 번, 또는 반복이 비교적 길면 더 자주 반복. 

  • 코드에서 operation을 중단하기가 상대적으로 쉬운 지점에서.
operation객체의 main메소드에서 취소 이벤트에 응답하는 방법에 대한 간단한 예제를 제공합니다. 이 경우, isCancelled메소드는 while루프를 통해 매번 호출 되므로, operation이 시작되기 전에 빠른 종료가 가능하고, 일정한 간격으로 다시 반복됩니다.

class ZeddOperation: Operation {

    override func main() {

        while(!self.isCancelled && !isFinished) {

            // Do some work and set isDone to YES when finished

        }

    }

}


앞의 예제에서는 cleanup코드가 없지만, 사용자 정의 코드는 사용자 정의 코드에 의해 할당된 모든 자원을 확보해야합니다.


후..아니 원래 코드는 Objc인데...여기에는 isDone으로 되어있는데...isDone은 없는데... isFinish겠죠?



그리고 저는 이 main이 왜 필요한지 아직 잘 모르겠는데..아 너무 어렵네요 ㅠㅜㅜmain의 역할을 먼저 설명해주면 좋을텐데 밑에서 설명하긴 하는데 왜 순서를 이렇게 해놓은건지!?!?!?


Configuring Operations for Concurrent Execution

operation객체는 기본적으로 동기식(synchronous)으로 실행됩니다. 즉 start메소드를 호출하는 쓰레드에서 task를 수행합니다. operation queue는 nonconcurrent operation의 쓰레드를 제공하기 때문에, 대부분의 operation은 여전히 비동기적(asynchronously)으로 실행됩니다. 그러나 operation을 수동으로 실행하고 비동기적으로 실행하려는 경우, 적절한 작업을 수행하여 이를 수행해야합니다. 


또 잘 이해가 안가죠....

그러니까 저~~~~~위에서 Concurrent Versus Non-concurrent Operations부분에서, 

"NSOperation클래스의 isConcurrent메소드는 start메소드가 호출 된 쓰레드와 관련하여 operation이 동기적/비동기적으로 실행되는지 여부를 알려줍니다. 기본적으로 이 메소드는 False반환합니다”라고 그랬죠? false를 반환하면 isConcurrent -> 너 Concurrent니? -> false반환 == 아니; nonConcurrent임ㅎ == 아니 나 동기적(synchronously)으로 수행되는데..;
그러니까 기본적으로 synchronously로 수행되니까 첫줄에서 operation객체는 기본적으로 동기식(synchronous)으로 실행됩니다.라고 한거에요. 하지만 operation queue에 넣으면 concurrent로 실행되니까 "대부분의 operation은 여전히 비동기적(asynchronously)으로 실행됩니다”라고 한 것 같아요. 

근데 내가 수동으로 실행한다는 말은..start를 해준다 뭐 그런건데, 이건 operation queue에 넣지 않는? 그런거같은데..! 그럼 결국 기본적으로 synchronously로 수행되니까 synchronously하게 실행되겠죠. 하지만 내가 수동으로 실행은 하고 싶은데!!!!!!!!!!!!!!!!! asynchronously하게 실행하고 싶으면!!!!!!!!!!!!!!적절하게 내가 코드를 추가하던가 해서 이를 수행해야한다는 거에요. 위에서도 설명했는데, 혹시 또 이해가 안가실까봐 ㅎ Apple은 참 똑같은 말을 다르게,,,또,,말하는,,,그런게 있으니까...
(근데 제 해석이 다를 수도 있어용!!! 혹시 틀렸다면 제발..꼭 말씀해주시길 바랍니다!!!!:0)

다음은 concurrent operation을 위해 override해야하는 메소드들입니다.

 start : (Required. 필수) 모든 concurrent operation에서 이 메소드를 override하고 기본 동작을 사용자 지정 구현으로 바꿔야합ㄴ디ㅏ. 수동으로 operation을 실행하려면, start메소드를 호출합니다. 따라서 이 메소드를 구현하면, operation의 시작점이 되며, operation을 실행하는 쓰레드나 다른 실행 환경을 설정할 수 있습니다. 구현에 언제든지 super를 호출하면 안됩니다. (Your implementation must not call super at any time.)

 main : (Optional. 선택사항) 이 메소드는 일반적으로 operation객체와 연결된 task를 구현하는데 사용됩니다. start메소드에서 task를 수행 할 수는 있지만, 이 메소드를 사용하여 task를 구현하면 setup 및 task코드를 더 깔끔하게 분리할 수 있습니다.


 isExecutingisFinished :

: (Required. 필수) Concurrent operation은 실행환경을 설정하고 해당 환경의 상태(state)를 외부 클라이언트에 보고해야합니다. 따라서 Concurrent operation은 일부 상태 정보를 유지 관리하여 해당 task를 언제 실행하고 언제 해당 task를 완료했는지 확인해야합니다. 그런 다음 이 메소드를 사용하여 해당 state를 보고해야합니다.

이 메소드의 구현은 다른 쓰레드에서 동시에 호출하는 것이 안전해야합니다. 또한 이러한 방법으로 보고 된 값을 변경 할 때 예상 key path에 대한 적절한 KVO notifications을 생성해야합니다.


  isConcurrent : (여러분 isAsynchronous로 생각하시면 됩니다.) (Required. 필수) operation을 concurrent operation으로 식별(identify)하려면 이 메소드를 override하고, true를 리턴하세요. (기본적으로 false라고 그랬죠?) 이 프로퍼티가 true를 리턴하게 해야만 너 Concurrent니? -> ㅇㅇ == Asynchronous임


Maintaining KVO Compliance

NSOperation클래스는 다음 Key-path에 대해 key-value observing(KVO)를 준수합니다.

start메소드를 override하거나 main을 override하는 것 이외의 다른 NSOperation객체를 커스터마이징 하는 경우, 커스텀 객체가 key-path에 대해 KVO를 준수하는지 확인해야합니다. start메소드를 override할 때 가장 염려해야하는 key-path는 isExecuting 및 isFinished입니다. 이러한 메소드를 다시 구현하면 가장 일반적으로 영향을 받는 key-path입니다.


다른 operation객체 이외의 것에 대한 의존성 지원을 구현하려는 경우, isReady메소드를 override하고, 사용자 정의 종속성이 충족 될 때 까지 강제로 false를 리턴할 수도 있습니다. (사용자 지정 종속성을 구현하는 경우, NSOperation클래스에서 제공하는 기본 종속성 관리 시스템을 계속 지원하고 싶으면, isReady메소드에서 super를 호출하세요.) 이러한 operation객체의 상태가 변경되면, isReady key-path에 대한 KVO notifications을 생성하고 변경사항을 보고합니다. addDependency : 또는 removeDependency : 메소드를 override하지 않는 한, 종속 key-path에 대한 KVO notifications생성에 대해 걱정 할 필요가 없습니다.

NSOperation의 다른 주요 path에 대해 KVO notifications을 생성 할 수는 있지만, 그렇게 할 필요는 없습니다. operation을 취소해야하는 경우, 기존 cancel메소드를 호출하여 operation을 취소 할 수 있습니다. 마찬가지로 operation객체에서 queue priority정보를 수정할 필요가 거의 없어야 합니다. 마지막으로, 당신의(?..귀하의?..) concurrency status를 동적으로 변경 할 수 없다면, isConcurrent key-path에 KVO notifications을 제공할 필요가 없습니다.

Customizing the Execution Behavior of an Operation Object

operation객체의 구성(configuration)은 operation객체를 작성한 후에 큐에 추가하기 전에 발생합니다. 이 섹션에서 설명한 configuration타입은 사용자가 NSOperation을 직접 서브클래싱 했는지 또는 기존 서브 클래스를 사용했는지에 관계없이 모든 operation객체에 적용 할 수 있습니다.

Configuring Interoperation Dependencies

종속성은 별개의 operation객체의 실행을 직렬화 하는 방법입니다.

다른 operation에 종속된 operation은 해당 작업에 필요한 모든 작업이 완료될 때 까지 실행을 시작할 수 없습니다. 따라서 종속성을 사용하여 두 operatio객체 간에 단순한 일대일 종속성을 생성하거나 복잡한 객체 종속성 그래프를 작성 할 수 있습니다.


두 operation객체 간에 종속성을 설정하려면 NSOperation의 addDependency : 메소드를 사용합니다. 이 메소드를 현재 operation객체에서 매개변수로 지정한 대상 operation까지 단방향 종속성을 만듭니다. 이 종속성은 대상 객체의 실행이 완료 될 때 까지 현재 객체를 실행 할 수 없음을 의미합니다. 종속성은 동일한 큐의 operation으로 제한되지 않습니다. operation객체는 자체 종속성을 관리하므로, operation간의 종속성을 작성하고 이들을 모두 다른 큐에서 추가할 수 있습니다. 그러나 허용되지 않는 한가지 방법은 operation간에 순환 종속성을 만드는 것입니다. 이렇게 하면 영향을 받는 operation이 실행되지 않도록 하는 프로그래머 에러가 발생합니다.(진짜 programmer errror라고 나와있는데..프로그래머 에러가 뭐지..)


operation의 종속성이 모두 실행되면, 일반적으로 operation객체는 일반적으로 실행 할 준비가 됩니다.(becomes ready to execute.) isReady메소드의 behavior를 사용자 지정하면, operation의 준비 상태는 설정 한 조건에 따라 결정됩니다. operation객체가 큐에 있으면, 해당 큐는 언제든지 해당 operation의 실행을 시작할 수 있습니다. 수동으로 operation을 실행하려면 start메소드를 호출해야합니다.


important : operation을 실행하거나 operation queue에 추가하기 전에 항상 종속성을 구성해야합니다. 나중에 추가된 종속성은 주어진 operation객체가 실행되는 것을 막지 못할 수 있습니다.


종속성은 객체의 상태가 변경 될 때마다 적절한 KVO notifications을 보내는 각 operation객체에 의존합니다. operation객체의 동작을 사용자 지정하는 경우, 종속성에 문제가 발생하지 않도록 사용자 지정 코드에서 적절한 KVO notifications을 생성해야 할 수 있습니다. 


Changing an Operation’s Execution Priority

큐에 추가된 operation의 경우, 먼저 대기중인 operation의 작업 준비 상태에 따라 실행 순서가 결정 된 다음, 상대적 Priority에 따라 실행순서가 결정됩니다. 준비상태는 다른 operation에 대한 operation 종속성에 의해 결정되지만, Priority levels은 operation객체 자체의 특성(attribute)입니다. 기본적으로 모든 새로운 operation객체는 “normal”의 Priority를 갖지만, 객체의 setQueuePriority메소드를 호출하여 필요에 따라 해당 Priority를 높이거나 낮출 수 있습니다.


Priority levels은 동일한 operation queue에 있는 operation에만 적용됩니다. 앱에 여러개의 operation queue가 있는 경우, 각각은 다른 operation queue와 독립적으로 자체 operation Priority를 지정합니다. 따라서 낮은 Priority operation이 다른 큐에서 Priority가 높은 operation보다 먼저 실행될 수 있습니다. (중요)


Priority levels은 종속성을 대체하지 않습니다. (Priority levels are not a substitute for dependencies) Priority는 operation queue에서 현재 준비되어 있는 operation에만 실행하기 시작하는 순서를 결정합니다. 예를들어, 큐에 Priority가 높은 operation과 Priority가 낮은 operation이 모두 포함되어 있고, 두 operation이 모두 준비되면 큐는 Priority가 높은 operation을 먼저 실행합니다. 그러나 Priority가 높은 operation이 준비되지 않았고, Priority가 낮은 operation이 준비된 상태라면, 큐는 Priority가 낮은 operation을 먼저 실행합니다. 다른 operation이 완료될 때 까지 한 operation이 시작되지 않도록 하려면 종속성을 사용해야합ㄴ디ㅏ)


Changing the Underlying Thread Priority

 OS X v10.6이상에서는 operation의 기본 쓰레드의 Priority를 구성 할 수 있었습니다. 시스템의 쓰레드 정책은 커널자체에서 관리하지만, 일반적으로 Priority가 높은 쓰레드는 Priority가 낮은 쓰레드 보다 실행 할 기회가 더 많습니다. 연산객체에서 쓰레드 Priority는 0.0에서 1.0사이의 부동소수점 값으로 지정하며, 0.0은 가장낮은 Priority고, 1.0은 가장 높은 Priority입니다. 명시적 쓰레드 우선순위를 지정하지 않으면 기본적으로 Priority는 0.5로 실행됩니다.


operation의 쓰레드 Priority를 설정하려면 operation객체의 setThreadPriority메소드를 호출하여 큐에 추가하거나 수동으로 실행해야합니다. operation을 실행 할 시간이 되면, 기본 start메소드는 지정한 값을 사용하여 현재 쓰레드의 Priority를 수정합ㄴ다. 이 새로운 Priority는 operation의 main 메소드에 대해서만 유효합니다. 다른 모든 코드(operation의 completion 블록 포함)은 기본 쓰레드 Priority(=0.5라고 그랬죠?)로 실행됩니다. concurrent operation을 생성하고, start메소드를 override하는 경우, 반드시 쓰레드 Priority를 직접 구성해야합니다.(you must configure the thread priority yourself.)


Setting Up a Completion Block

OS X v10.6및 이후 버전에서는 main task가 완료되면, operation이 completion 블록을 실행 할 수 있스빈다. completion 블록을 사용하여 main task의 일부로 간주하지 않는 작업을 수행 할 수 있씁니다. 예를들어, completion 블록을 사용하여 operation객체가 완료되었음을 관련 클라이언트에 알릴 수 있습니다. concurrent operation 객체는 completion블록을 사용하여 최종 KVO notifications(final KVO notifications)을 생성 할 수 있습니다.

completion 블록을 설정하려면, NSOperation의 setCompletionBlock메소드를 사용하세요. (Swift에서는 

completionBlock입니당.) 이 메소드에 전달하는 블록에는 인수가 없고 반환값이 없어야 합니다.


Tips for Implementing Operation Objects

operation객체는 구현하기 쉽지만 코드를 작성하는 과정에서 알아야 할 몇가지 사항이 있습니다. 다음 섹션에서는 operation객체에 대한 코드를 작성 할 때 고려해야할 요소에 대해 설명합니다. 

Avoid Per-Thread Storage

대부분의 operation은 쓰레드에서 실행되지만, nonconcurrent operation의 경우, 해당 쓰레드는 일반적으로 operation queue에서 제공됩니다. operation queue에서 쓰레드를 제공하느 ㄴ경우, 해당 쓰레드가 큐에 속해있고, operation에 의해 영향을 받지 않는 것으로 간주해야합니다. 특히, 직접 만들지 않거나 관리하지 않는 쓰레드에 어떤 데이터도 연결해서는 안됩니다. operation queue에 의해 관리되는 ㅅ쓰레드는 시스템 및 앱의 필요에 따라 달라집니다. 따라서 쓰레드 단위 저장소를 사용하는 operation간에 데이터를 전달하는것은 신뢰할 수 없으며 실패할 수 있습니다. 

operation객체의 경우, 어떤 경우에도 쓰레드 별 저장소를 사용 할 이유가 없어야 합니다. operation객체를 초기화 할 때는 객체에 operation을 수행하는데 필요한 모든 것을 제공해야합니다. 따라서 operation객체 자체가 필요한 컨텍스트 저장소를 제공합니다. 들어오고 나가는 모든데이터는 앱에 다시 통합되거나 더이상 필요하지 않을 때 까지 거기에 저장되어야 합니다.

Keep References to Your Operation Object As Needed

operation객체가 비동기적으로 실행되기 때문에, operation객체를 만들고 잊어버릴 수 있다고 가정해서는 안됩니다. 그것들은 여전히 “객체”이며 코드에 필요한 참조를 관리하는 것은 당신에게 달려있습니다. operation이 완료된 후, operation에서 결과 데이터를 검색해야하는 경우 특히 중요합니다.


operation에 대한 참조를 항상 유지해야하는 이유는 나중에 객체를 큐에 요청할 기회가 없을 수 있기 때문입니다. 큐는 최대한 빨리 operation을 전달하고 실행 할 수 있도록 최선을 다합니다. 대부분의 경우, 큐는 operation이 추가 된 직후에 operation실행을 시작합니다. 자신의 코드가 operation에 대한 참조를 얻기 위해 큐로 돌아갈 때 까지 이 operation은 이미 완료되고 큐에서 제거될 수 있습니다


Handling Errors and Exceptions

operation은 기본적으로 앱내부의 개별 엔티티이므로, 발생하는 모든 오류나 예외를 처리해야합니다. OS X v10.6이상에서는 NSOperation클래스가제공하는 기본 start메소드가 예외를 catch하지 않습니다. (OS X v10.5에서 start 메소드는 예외를 포착하고 억제합니다.) 자신의 코드는 항상 예외를 catch하고 직접 억제(suppress)해야합ㄴ니다. 또한 오류 코드를 확인하고 필요에 따라 앱의 해당 부분에 알립니다. 그리고 start메소드를 override하는 경우, 사용자 정의 구현에서 예외를 catch하여 기본 쓰레드 범위를 벗어나지 않도록 해야합니다. 오류 상황의 케이스중에는 다음과 같은 것들이 있습니다. 


  • UNIX errno-스타일의 오류 코드를 확인하고 처리하십시오
  • 메소드와 함수에 의해 리턴 된 명시 적 오류 코드를 점검하십시오

  • 자신의 코드 또는 다른 시스템 프레임워크에 의해 발생한 예외를 파악하십시오.
  • 다음 상황에서 예외를 throw하는 NSOperation 클래스 자체에서 발생하는 예외를 Catch합니다.

    • operation이 실행준비가 되지 않았는데, start메소드가 호출되었을 때
    • operation이 실행중이거나 완료되었을 때(취소되어서 완료되었을 수 있음) 해당 start메소드가 다시 호출되었을 때
    • 이미 실행중이거나 완료된 operation에 completion블록을 추가하려고 했을 때
    • 취소된 NSInvocationOperation객체의 결과를 검색하려고 했을 때. (NSInvocationOperation은 뭐 disable됐으니(swift에서) ㅎ....)
사용자 정의 코드에 예외 또는 오류가 발생하면, 해당 오류를 앱의 나머지 부분에 전파하는데 필요한 모든 단계를 수행해야합니다. NSOperation클래스는 앱의 다른 부분에 오류 결과 코드나 예외를 전달하는 명시적인 메소드를 제공하지 않습니다. 따라서 그러한 정보가 당신의 앱에 중요하다면, 필요한 코드를 제공해야합니다. 


Determining an Appropriate Scope for Operation Objects

임의로 많은 수의 연산을 operation queue에 추가 할 수는 있지만, 실제로는 비실용적입니다. 다른 객체와 마찬가지로 NSOperation클래스의 인스턴스는 메모리를 소비하며 실행(execution)과 관련된 실제 비용을 가집니다. 각 operation객체가 적은 양의 작업을 수행하고, 수만개를 만들면 실제 작업을 수행하는 것보다 operation을 dispatching(파견?)하는데 더 많은 시간을 소비 할 수 있습니다. 또한 앱 메모리가 이미 제약되어 있는 경우, 메모리에 수만개의 operation객체만 있으면 성능이 더욱 저하될 수 있습니다.


operation 을 효율적으로 사용하는 핵심은 수행해야하는 작업량과 컴퓨터를 계속 사용하는 사이에 적절한 균형을 찾는 것입니다. operation이 적절하게 수행되도록 하세요. 예를들어, 앱이 100개의 서로 다른 값에 대해 동일한 작업을 수행하는 operation객체 100개를 생성하는경우, 10개의 operation객체를 생성하여 각각 10개의 값을 처리하는것이 좋습니다. 

또한 많은 수의 operation을 한번에 큐에 추가하거나 operation객체를 처리하는 속도보다 더 빠르게 큐에 지속적으로 추가하지 않아야합니다. 큐에 operation 객체를 플러딩 하는 대신, 이러한 객체를 일괄적으로 작성하세요. 하나의 배치가 실행되면 completion블록을 사용하여 앱에 새 배치를 작성하도록 지시하세요. 할 일이 많은 경우, 큐가 충분한 operation으로 채워져 있어 컴퓨터가 사용중이 상태로 유지되지만, 한번에 많은 operation을 만들어서 메모리가 부족한 상태로 만들지 마세요.


물론 생성하는 operation객체의 수와 각 객체에서 수행하는 작업의 양은 가변적이며, 앱에 전적으로 의존합니다. 항상 효율성과 속도 사이에서 적절한 균형을 찾을 수 있도록 Instruments과 같은 도구를 사용해야합니다. 



Executing Operations


궁극적으로, 앱은 관련 작업을 수행하기 위해 operation을 실행해야합니다. 이 섹션에서는 런타임에 operation실행을 조작할 수 있는 방법뿐만아니라 operation을 실행하는 몇가지 방법을 학습합니다.


Adding Operations to an Operation Queue

operation을 실행하는 가장 쉬운 방법은 NSOperationQueue클래스의 인스턴스인 operation queue를 사용하는 것입니다. 앱은 사용하려는 operatio queue를 작성하고 유지보수해야합ㄴ디ㅏ. 

앱에는 임의의 수의 큐가 있을 수 있지만, 특정 시점에 얼마나 많은 operation이 실행될 수 있는지에 대한 실질적인 제한이 있습니다. operatio queue는 시스템과 함께 작동하여 동시 operation수를 사용가능 한 코어 및 시스템 부하에 적합한 값으로 제한합니다. 따라서 추가 큐를 만드는 것이 추가 operation을 실행 할 수 있는 것은 아닙니다.(헐...ㄷㄷ그렇군여)

큐를 만들려면 다른 객체처럼 앱에 할당합니다.


let queue = OperationQueue()


(문서에는 Objc코드로 나와있지만 그냥 Swif로..할게용!!)

operation을 큐에 추가하려면, addOperation메소드를 사용합니다. 

OS X v10.6 이상에서는 addOperations(_:waitUntilFinished:)메소드를 사용하여 operation 그룹을 추가하거나 addOperation(_:)메소드를 사용하여 해당 operation객체가 없는 큐에 블록 객체를 직접 추가 있습니다. 이러한 메소는 operation (또는 operations) 큐에 추가하고 처리를 시작해야함을 큐에 알립니다. 대부분의 경우 operation 큐에 추가된 직후에 실행되지만, 몇ㄹ가지 이유로 큐의 operation 실행이 지연될 있습니다. 특히 대기중인 operation 아직 완료되지 않은 다른 operation 종속되어 있으면, 실행이 지연될 있습니다. (앞에서 배운거죠??) operation queue자체가 일시중단 되었거나 이미 최대 동시 operation수를 수행중인경우, 실행이 지연될 수도 있습니다. 다음에는 operation 큐에 추가하기 위한 기본 구문을 보여줍니다.

let queue = OperationQueue()

queue.addOperation {

          for index in 1...10 {

               print("zedd\(index)")

    }

}

 

 operation객체를 큐에 추가하기 전에 필요한 모든 configuration 수정 작업을 수행해야합니다. 추가된 후에는 언제든지 operation 실행될 있기 때문에 변경사항이 의도한 효과를 미치기에는 너무 늦을 있습니다.


NSOperationQueue클래스는 operation 동시 실행을 위해 설계되었지만, 한번에 하나의 작업만 실행하도록 단일 큐를 강제 실행 있습니다. 

maxConcurrentOperationCount메소드를 사용하면 operation 객체에 대한 최대 동시 operation수를 구성 있습니다. (아니 근데 프로퍼티 아닌가...계속 method라고 언급하네요. NSOperationQueue 인스턴스 프로퍼티가 맞아용!) 메소드에 1 지정하면, 큐는 한번에 1개의 operation 실행합니다. 한번 하나의 operation 실행 있지만, 행순서는 operation 준비상태와 할당된 priority 같은 다른 요소를 기반으로 합니다. 따라서, serialized operation queue(직렬화된 operation queue?) Grand Central Dispatch serial dispatch queue 거의 동일한 기능을 제공하지 않습니다. operation객체의 실행순서가 중요한 경우, operation 큐에 추가하기 전에 종속성을 사용하여 순서를 결정해야합니다


Executing Operations Manually

operation queueu가 operation객체를 실행하는 가장 편리한 방법이지만, 큐를 사용하지 않고 operation을 실행 할수도 있습니다. 그러나 수동으로 operation을 실행하도록 선택하면, 코드에서 취해야 할 예방조치가 있습니다. 특히 
operation을 실행할 준비가 되어 있어야 하며 항상 start메소드를 사용하여 operation을 시작해야합니다.
isReady메소드가 true를 리턴 할 때 까지 operation은 실행 가능한 것으로 간주되지 않습니다. isReady메소드는 NSOperation클래스의 종속성 관리 시스템에 통합되어 operation종속성의 상태를 제공합니다. 종속성이 지워지는 경우에만 실행을 시작할 수 있습니다.

수등오로 operation을 실행할 때는 항상 start메소드를 사용하여 실행을 시작해야합니다. start메소드는 사용자 지정 코드를 실행하기 전에 여러 안전성 검사를 수행하기 때문에 main또는 다른 메소드 대신 이 메소드를 사용합니다. 특히 default start메소드는 operation에서 종속성을 올바르게 처리하는데 필요한 KVO notifications을 생성합니다. 이 메소드는 또한, 이미 취소된 경우 operation실행을 올바르게 방지하고, operation을 실제로 실행할 준비가 되지 않은 경우 예외를 throw합니다.

앱이 concurrent operation 객체를 정의하는 경우, 실행하기 전에 isConcurent(isAsynchronous와 같다고 그랬죠?)메소드를 호출하는 것도 고려해야합ㄴ디ㅏ. 이 메소드가 false를 리턴하는 경우, local code는 현재 쓰레드에서 동기적으로 operation을 실행할 지 아니면 별도의 쓰레드를 먼저 작성할지를 결정 할 수 있습니다. 그러나 이런 종류의 검사를 구현하는것은 전적으로 당신에게 달려있습니다.

Canceling Operations

operation queue에 추가된 operation객체는 큐에 의해 효과적으로 소유되므로 제거 할 수 없습니다. operation을 큐에서 제거하는 유일한 방법은 operation을 취소하는 것입니다. cancel메소드를 호출하여 하나의 개별 operation객체를 취소하거나 큐 객체의 cancelAllOperations메소드를 호출하여 큐에 있는 모든 operation객체를 취소할 수 있습니다.


더이상 필요가 없다고 확신하는 경우에만 operation을 취소해야합니다. 취소 명령을 실행하면 operation객체가 “canceled”상태가 되어 실행되지 않게 됩니다. 취소된 canceled은 여전히 “finished(완료된)”것으로 간주되므로, 종속된 객체는 해당 KVO notifications을 수신하여 해당 종속성을 지웁니다. 따라서 선택적으로 operation을 취소하는대신, 앱을 종료하거나 사용자가 취소를 특별히 요청한 것과 같은 중요한 이벤트에 대한 응답으로 대기중인 모든 operation을 취소하는 것이 더 일반적입니다. 


Waiting for Operations to Finish

최상의 성능을 얻으려면, operation실행중에 앱을 추가로 자유롭게 사용할 수 있도록 operation을 가능한 한 비동기적으로 설계해야합니다. operation을 생성하는 코드가 해당 operation의 결과도 처리하는 경우, operation이 완료될 때 까지 NSOperation의 waitUntilFinished메소드를 사용하여 해당 코드를 차단 할 수 있습니다. 일반적으로 이 방법을 사용하면 도움이 될 수 있습니다. 현재 쓰레드를 차단하는 것이 편리한 솔루션일 수 있지만, 코드에 직렬화를 더 많이 도입하고 동시성의 전체 양을 제한합니다. (but it does introduce more serialization into your code and limits the overall amount of concurrency.)


중요 : 앱의 main쓰레드에서 operation을 기다리지 않아야 합니다. 보조(secondary) 쓰레드 또는 다른 operation에서만 수행해야합니다. main쓰레드를 차단하면 앱이 사용자 이벤트에 응답하지 못하게 되어 앱이 응답하지 않을 수 있습니다.
(오 이거 진짜 중요하죠..!!)

단일 operation이 끝나기를 기다리는 것 이외에도 NSOperationQueue의 waitUntilAllOperationsAreFinished메소드를 호출하여 큐의 모든 operation을 기다릴 수 있습니다. 전체 큐가 끝날 때 까지 기다릴 때, 앱의 다른 쓰레드가 여전히 큐에 operation을 추가 할 수 있으므로 대기 시간이 길어질 수 있습니다.(오..)

Suspending and Resuming Queues

operation 일시적으로 중단하려면  isSuspended메소드( 자꾸 메소드라 그러지>??) 사용하여 해당 operation queue 일시중단 있습니다. 일시중단해도 이미 실행중인 operation 일시중지 되지는 않습니다. 단순히 새로운 operation 실행을 예약하지 못하게 합ㄴ디ㅏ. 진행중인 operation 일시중지하라는 사용자의 요청에 대한 응답으로 큐를 일시중단 있습니다. 사용자가 최종적으로는 operation 재개하려고 것이기 때문입니다


끄아아ㅏ앙ㅇ 와 정말 기네요 Operation...사실 Operation Queue나 이런걸 한번도 사용해보질 않아서...진짜 어려웠는데 그래도 잼네요 키키 문서 한번 쫙 읽는 것을 추천드려용..! 아무튼 오늘도 도움이 되었길 바랍니다 :)

근데 진짜 틀린 부분이 있을지도 몰라요 혹시 틀린 부분이 있다면 댓글이나 PC화면 오른쪽 하단에 있는 채널서비스를 통해 메세지 주세요!!


반응형