[Objective-C] 블럭 문법 (Blocks Programming)
최근 iOS나 OS X SDK Framework 를 보고 있다면 블럭에 기반한 메소드들이 점점 늘어나는 것 같다. 상황에 따라서 들여쓰기 레벨이 높아지거나 좀 불안한(?) 코드 모양이 나오는 듯 코드 리딩에 안좋은 모양새를 나타낼 때도 있지만, 그래도 여러 면에서 유용한 기능(?)이니 이 참에 메모를 남겨본다.
블럭(Block)은 C 함수이다. 정확히 표현하자면 런타임에 생성되는 다이나믹 함수(Dynamic Function) 이다. 여기서 런타임(Runtime)과 다이나믹(Dynamic) 이란 의미에 중점을 두면 '병렬처리'나 '비동기처리(Asyncronous Processing)', 혹은 '함수형언어(Functional Language)'의 특징과 비슷하다.
근데 말이 너무 어렵다 -_-
어쨌든 그래서 어떻게 쓰이는지 한번 보자. 참고로 코드 예제는 애플의 블럭 프로그래밍 문서의 것을 그대로 활용한다.
이번에는 블럭의 제한에 대해 보자.
이 문제를 해결하려면 블럭 내부에서 사용한다고 표현을 고치면 된다. x 선언부를 아래와 같이 수정한다.
일반적으로 많이 쓰이는 구조
이론적인 예제는 이제 집어치우고 실제로 많이 쓰이는 방식을 살펴보자. 주로 비동기(Asynchronous) 방식으로 처리되어야 하는 경우 기존에는 Delegate 스타일이 많이 쓰였지만 요즘엔 한 번의 결과만을 받는 경우 이 블럭 방식을 많이 사용한다.
아래 예제는 블럭 타입과 블럭을 사용하는 메서드의 선언(Definition, 즉 헤더파일) 예제이다.
이 메서드의 구현부(Implementation, 즉 .m 파일)는 몇 가지 가정이 있어야 할 것이다. 비동기로 처리되어 responseBlock이 호출되어야 한다는 점이다. 아래 코드는 특정 클래스의 메서드를 실행시키고 자신은 delegate로써 이 결과물을 따로 호출받는 식으로 동작한다는 가정하의 예제이다.
그리고 위 처럼 strong property 에 블럭을 묶어두고 있는 경우 처리가 끝나고 이를 nil로 초기화 해 줌으로써 해당 블럭을 계속 가지지 않도록 해야된다. 메모리가 남아돌아서 해제 안되도 문제 없다는 경우를 빼고 말이다. -_-;;
이 메서드를 사용하는 곳은 아래와 같은 식으로 블럭을 만들어 줄 수 있을 것이다.
이런 블럭 문법은 위 처럼 귀찮은 delegate 스타일을 간편하게 해결 할 수 있어서도 좋지만, GCD 등 여러 분야에서도 활용되는 중요한 문법이다. 더구나 GCD와 연동시키면 백그라운드/패러럴 프로세싱이 정말 즐거워 질 것이다.
[관련글] [Objective-C] Block Syntax 초간단정리
[관련글] [iOS] 특정 코드를 비동기로 실행시키기
블럭(Block)은 C 함수이다. 정확히 표현하자면 런타임에 생성되는 다이나믹 함수(Dynamic Function) 이다. 여기서 런타임(Runtime)과 다이나믹(Dynamic) 이란 의미에 중점을 두면 '병렬처리'나 '비동기처리(Asyncronous Processing)', 혹은 '함수형언어(Functional Language)'의 특징과 비슷하다.
근데 말이 너무 어렵다 -_-
어쨌든 그래서 어떻게 쓰이는지 한번 보자. 참고로 코드 예제는 애플의 블럭 프로그래밍 문서의 것을 그대로 활용한다.
int multiplier = 7; int (^myBlock)(int) = ^(int num) { return num * multiplier; };이 코드가 실행되면 myBlock 이라는 함수 변수(?)가 생성된다. 대충 C의 함수 포인터와 비슷하게 생각하자. 그래서 이런 식으로 실행시키는 것이 가능하다.
int result = myBlock(10); // result 결과는 70윗쪽꺽쇠(^)로 시작되는 것이 블럭 문법의 중요한 점이다. 즉 myBlock은 함수 블럭 변수이고 그 다음에 등장하는 '^(int num)'은 블럭 타입의 함수를 의미한다.
이번에는 블럭의 제한에 대해 보자.
int x = 123; void (^printXAndY)(int) = ^(int y) { x = x + y; // error printf("%d %d\n", x, y); };이 코드에서 에러가 발생한다는 주석이 달려있다. 이유는 블럭 외부의 변수에 뭔가를 쓰려고 해서이다. 즉 다른게 이야기 하자면, 블럭 내부에서는 블럭 외부의 변수를 읽기 전용(read only)으로만 참조 할 수 있다는 말이다.
이 문제를 해결하려면 블럭 내부에서 사용한다고 표현을 고치면 된다. x 선언부를 아래와 같이 수정한다.
__block int x = 123;언더스코프가 두 개나 붙어있어서 개인적으로 좀 싫어하는 표현이지만 그래도 Objective-C가 좋으니 어쩔 수가 없다. -_-;;
일반적으로 많이 쓰이는 구조
이론적인 예제는 이제 집어치우고 실제로 많이 쓰이는 방식을 살펴보자. 주로 비동기(Asynchronous) 방식으로 처리되어야 하는 경우 기존에는 Delegate 스타일이 많이 쓰였지만 요즘엔 한 번의 결과만을 받는 경우 이 블럭 방식을 많이 사용한다.
아래 예제는 블럭 타입과 블럭을 사용하는 메서드의 선언(Definition, 즉 헤더파일) 예제이다.
typedef void (^ResponseBlock)(NSData *data, NSError *error); ... @property (nonatomic, strong) ResponseBlock responseBlock; ... - (void)startRequestWithCompletion:(ResponseBlock)responseBlock;꺽쇠가 붙어있는 이름(ResponseBlock)이 블럭 타입 이름이 되었다. 프로퍼티(property)로 선언된 내용은 실제 블럭 함수를 보관해 놓기 위한 용도이고 strong 으로 정의함으로써 소유권을 이 클래스가 가지게 된다. 만약 strong이 아니라면 이 메서드를 호출한 쪽이 먼저 메모리에서 해제될 경우 문제가 발생 할 수도 있기 때문이다.
이 메서드의 구현부(Implementation, 즉 .m 파일)는 몇 가지 가정이 있어야 할 것이다. 비동기로 처리되어 responseBlock이 호출되어야 한다는 점이다. 아래 코드는 특정 클래스의 메서드를 실행시키고 자신은 delegate로써 이 결과물을 따로 호출받는 식으로 동작한다는 가정하의 예제이다.
// Method - (void)startRequestWithCompletion:(ResponseBlock)responseBlock { self.responseBlock = responseBlock; SomeClass *someObject = [[SomeClass alloc] init]; [someObject startRequestWithDelegate:self]; } ... // Delegate - (void)finishWithData:(NSData *)data error:(NSError *)error { if (self.responseBlock) { self.responseBlock(data, error); self.responseBlock = nil; } }많이 축약하긴 했지만 앞서 이야기한 가정 하에 구현했을 경우 위와 같은 모양이 될 것이다. startRequestWithCompletion: 메서드가 responseBlock을 보관하고 실제 필요한 작업을 시작한다. 그리고 작업이 끝나면 finishWithData:error: 라는 메서드로 호출이 들어오는데 여기서 저장해 놓은 responseBlock(블럭 함수)를 호출히서 결과가 끝났음을 알려준다.
그리고 위 처럼 strong property 에 블럭을 묶어두고 있는 경우 처리가 끝나고 이를 nil로 초기화 해 줌으로써 해당 블럭을 계속 가지지 않도록 해야된다. 메모리가 남아돌아서 해제 안되도 문제 없다는 경우를 빼고 말이다. -_-;;
이 메서드를 사용하는 곳은 아래와 같은 식으로 블럭을 만들어 줄 수 있을 것이다.
[someObject startRequestWithCompletion:^(NSData *data, NSError *error) { if (error) { ... } // process data }];심하게 축약하긴 했지만 어쨌든 블럭을 넘겨 줄 때 윗쪽 꺽쇠(^)로 시작하는 함수 형태를 기억해 놓자.
이런 블럭 문법은 위 처럼 귀찮은 delegate 스타일을 간편하게 해결 할 수 있어서도 좋지만, GCD 등 여러 분야에서도 활용되는 중요한 문법이다. 더구나 GCD와 연동시키면 백그라운드/패러럴 프로세싱이 정말 즐거워 질 것이다.
[관련글] [Objective-C] Block Syntax 초간단정리
[관련글] [iOS] 특정 코드를 비동기로 실행시키기
댓글