본문 바로가기
프로그래밍/iOS

애플리케이션 응답성 향상을 위해 동시성 사용하기

by 백룡화검 2012. 2. 7.

1. 애플이케이션에서의 응답성

아이폰, 아이패드를 포함한 모든 애플리케이션의 사용자 인터페이스에 있어서 응답성은  중요한 요소중에 하나이다.

많은 시간을 소비하는 태스크를 사용자 인터페이스가 동작하는 쓰레드에서 수행하면, 해당 태스크가 종료하기 까지

사용자는 어떠한 인터렉션도 행할 수가 없기 때문이다.  트위터의 글목록을 가져와서 보여주는 앱을 생각해보자.

최신글 목록과 사용자 프로필 이미지를 가져오는 동안, 버벅이 되며 먹통이 되면 누가 이 앱을 사용하겠는가?

이 포스팅에서는 Cocoa  Touch에서 지원하는 동시성 향상을  방법에 대해서 알아보겠다. 

MacOsX와 iOS에서 지원하는  사용자 인터렉션 응답성을 향상시키기 위한 다음과 같은 동시성 방법들이 있다.    
NSThread / NSObject가 지원하는 백그라운드/메인 쓰레드,  C기반의 POSIX 쓰레드,  비동기 기반의 메소드,  NSOperation /NSOperationQueue 등이 있다.  각각의  추상화 수준(사용 용이성)과 복잡성이 다르다.  추상화 수준이 높은 방법일 수록 
하드웨어 의존적인 부분을 감추어주고 쉽게 사용할 수 있어서, 비지니스 로직에 더 집중 할 수 있다.  좀더 세밀한 컨트롤과
뛰어난 성능을 원한다면 C 기반의 동시성 API 를 사용하자.   



2. NSOperation /  NSOperationQueue

웹에서 사진들의 목록을 받아와 이미지와 함께 사진의 이름을 출력하는 플리커 애플리케이션을 생각해보자.

사진들의 목록을 받아오고, 목록에 해당하는 이미지 각각을 로딩하는데 상당한 시간이 소모될 것이다. 이러한 작업을

아이폰 애플리케이션의 메인쓰레드에서 수행할 경우, 애플리케이션은 상당히 버벅거리게 된다. 백그라운드 쓰레드로 

이미지를 가져오는 방법을 선택하더라도 성능의 향상은 있겠지만 그 효과는 미미하다. 

하나의 이미지를 가져오는 작업에 하나의 쓰레드를 할당하여 완료되면 이미지를 목록을 추가하는 방식이 자연스러운 

애플리케이션이 될 것이지만,  몇 개의 쓰레드를 할당하는 것이 성능의 저하를 가져오지 않고 최적의 성능을 낼 것인지는

하드웨어에 의존적이어서 정확히 알 수가 없다.  아이폰에서 제공하는 비동기 방식의 메소드를 사용하여 해결 할 수 있지만, 

좀더 추상화 수준이 높으면서 하드웨어 변경에도 코드의 변경이 거의 없는 NSOperation/NSOperationQueue 를 사용해보자.

NSOperatinoQueue 는 내부적으로 최적의 성능을 낼 수는 쓰레드의 개수를  생성하고 관리해 준다. 

NSOperation은 NSObject를 상속하고, 하나의 작업단위를 캡슐화하는 추상클래스이다. 




NSOperation을 상속하여 커스텀 클래스를 작성하거나, NSBlockOperation , NSInvocationOperation을 
사용하여 작업을 캡슐화하고 NSOperationQueue에 삽입하면 별도의 쓰레드에서 태스크를 수행할 수 있다. 
이때 그래픽 사용자 인터페이스를 변경하는 작업은 반드시 메인쓰레드상에서 수행해야함을 주의하자!!
NSOperation을 상속하는 클래스는 main 이라는 이름의 메소드에서 독립적인 작업을 캡슐화한다.
NSInvocationOperation은 별도의 쓰레드에서 호출할 객체와 셀렉터를 지정할 수 있다.  



3. NSBlockOperation 

NSBlockOperation은 Lisp, Ruby와 같은 언어의 클로져와 유사한 Block 이라는 요소를 사용한다.  Block은 마치
데이터 스콥을 공유하는 함수와 유사하며, Block으로 지정한 코드를 캡슐화하여 별도 쓰레드로 동작시킨다.
NSOperation / NSInvocationOperation 과 NSOperationQueue를 사용하는 방법도 훌륭하다.  하지만 NSBlockOperation을
사용하면 동일한 성능의 장접을 갖되,  연관된 코드를 한곳에 집중 시킬수 있는  부수적인 장점이 생긴다.
그러나 아이폰 운영체제의 하위 호환성이 떨어지는 것이 단점이다.  



 블럭은 사용하는 코드는 아래와 같다. 

NSBlockOperation의 blockOperationWithBlock 메시지의 인자로 블럭을 정의하여 인자로 전달하였다. 

블럭 ^{} 의 내부에서  긴 시간이 소모되는 코드를 수행하고, 작업이 완료되면 MainThread 에서 GUI를 변경하도록 하고 있다.

블럭 내 코드는 별도의 쓰레드에서 실행되기 때문에 자체적인 릴리지 풀을 생성하여 사용해야한다. 마지막으로 블럭에 정의한

작업을 수행하기 위해 NSOperationQueue(workQueue)에 추가하고 있다. 

 

NSBlockOperation *fetchImageOp = [NSBlockOperationblockOperationWithBlock:^{

NSAutoreleasePool *localPool;

@try {
// Autorelease pool 생성
localPool = [[NSAutoreleasePool alloc] init];
// 시간이 소요되는 작업 수행 
[selfperformSelectorOnMainThread:@selector(storeImageForURL:) // 메인쓰레드에서 GUI 변경

withObject:result 

    waitUntilDone:NO];

}

@catch (NSException * exception) {

NSLog(@"Exception: %@", [exception reason]);

}

@finally {
// Autorelease pool 해제
[localPool release];

}

}];
// NSOperationQueue에 추가 
[workQueue addOperation:fetchImageOp];  

 


4. 블럭 선언 및 정의하기
다음과 같이 익명으로 정의되어, 메시지의 파라미터로 전달될 수 있다. 

int (^cubeIt)(int);// 블럭변수 선언    
cubeIt = ^(int num) { return num * num * num; };// 블럭정의


다음과 같이 익명으로 정의되어, 메시지의 파라미터로 전달될 수 있다.

(returnDataType (^) ( parameterType1, parameterType2, ...))blockName

'프로그래밍 > iOS' 카테고리의 다른 글

수동으로 UI 컨트롤에 이벤트 전달  (0) 2012.02.07
[iPhone] 로깅 프레임웍 cocoalumberjack  (0) 2012.02.07
애플 푸시 서비스  (0) 2012.02.07
푸시 메시지 포맷  (0) 2012.02.07
NSNotification  (0) 2012.02.07