Languages/Objective-C2011.01.04 16:19
[이전 글에 이어서..]

그럼 새로운 UIView 애니메이션은 어떻게 써먹느냐. Ruby 같은 프로그래밍 언어를 보면 함수를 실행할 때 그 함수에 다른 함수 바디를 동적으로 만들어서 넘길 수도 있도록 허용하고 있는데요. Objective-C에서도 이제 그런 식의 프로그래밍이 가능합니다. 우선 developer.apple.com에서 퍼온 API 설명부터 한번 보죠.

animateWithDuration:animations:completion:

Animate changes to one or more views using the specified duration and completion handler.

+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
Parameters
duration

The total duration of the animations, measured in seconds. If you specify a negative value or 0, the changes are made without animating them.

animations

A block object containing the changes to commit to the views. This is where you programmatically change any animatable properties of the views in your view hierarchy. This block takes no parameters and has no return value. This parameter must not be NULL.

completion

A block object to be executed when the animation sequence ends. This block has no return value and takes a single Boolean argument that indicates whether or not the animations actually finished before the completion handler was called. If the duration of the animation is 0, this block is performed at the beginning of the next run loop cycle. This parameter may be NULL.

Discussion

This method performs the specified animations immediately using the UIViewAnimationOptionCurveEaseInOut and UIViewAnimationOptionTransitionNone animation options.

For example, if you want to fade a view until it is totally transparent and then remove it from your view hierarchy, you could use code similar to the following:


이 함수는 static method이고, 첫 번째 인자로는 Animation 지속시간, 두 번째 인자로는 애니매이션이 수행되는 동안 행해질 작업에 대한 코드 블럭, 그리고 세 번째 인자로는 애니메이션이 끝나면 실행될 코드 블럭이 넘어갑니다.

대충 어떻게 써먹느냐 하면...

[UIView animatedWithDuration:0.2
            animations:^{view.alpha = 0.0;}
            completion:^(BOOL finished){ 
                               if ( finished)
                                  [view removeFromSuperView];
                             }];

위의 코드는 0.2 초 에 걸쳐, 어떤 뷰를 서서히 투명하게 만들고, 투명화 작업이 끝나면 그 뷰를 부모 뷰에서 떼 버리는 코드입니다. 블럭의 시작은 ^으로 표시하고, 인자의 목록은 () 안에, 블럭 코드 바디는 {} 안에 둔다는 것을 알 수 있습니다.

이렇게 코딩하면 앞서 봤던 [UIView beginAnimations], [UIView commitAnimations]를 통해 구현한 경우보다 코드 사이즈가 줄어듭니다. 애니메이션 종료시 호출될 메소드를 따로 구현해 둘 필요가 없거든요. 블럭 문법에 익숙한 분들에게는 코드도 훨씬 더 깔끔해 보이죠.



신고
Posted by 이병준

소중한 의견, 감사합니다. ^^

Extremely Agile2010.01.06 17:18

이 글은 공중곡예사님의 2009년 12월 17일에서 2010년 1월 6일까지의 미투데이 내용입니다.

최근에 하고 있는 짓들이란게 이런거라는...ㅎㅎ
그래서 글도 잘 못쓰고 있습니다.
이 블로그에 가끔 들러주는 분들께 죄송할 따름...

다들 눈 많이 내리는 한겨울에 별 탈 없이 지내고 계신지...

저는 보시다시피 당분간은 아이폰 응용 개발이나
논문 작성 때문에 바쁠 예정이라 블로그에는 좀 소홀할 듯 싶습니다.

이 한 겨울에 다들 건강 조심하시고...

이런 식으로라도 가끔 글 남기도록 하겠습니다.

아이팟 터치가 두대가 생겨서 그래도 할 수 있는 것들이 좀 늘어나 즐겁네요.

대박까진 몰라도 뭔가 새로운 것들을 배울 수 있는 기회가 되었으면 좋겠습니다.
새로운 것을 배운다는 것은 언제나 즐거운 일이니까요.

(그래도 Objective-C에는 친숙해지기가 너무나 어렵군요. ㅎㅎ)

논문은 Consistent Hashing을 활용한 failover system 구현에 관한 것입니다.
잘 될지는 모르겠지만... 2월이 마감이라 이번 한달 동안은 꽤나 바쁠 것 같군요.
이제 실험도 착수해야하고... 논문도 고쳐야 하니까요.

사실 실험에는 젬병인데...
어떻게 해야 할지 고민도 많이 되는군요.

나이들어 박사학위 과정에 계속 머물러 있는 것도 부담이라
이번에는 어떻게든 끝내야 할텐데....

신고
Posted by 이병준

소중한 의견, 감사합니다. ^^

  1. duru

    새해 첫 과업이 논문 통과군요.
    희망하시는 모든 일 다 이룰 거라 믿어 의심치 않습니다.^^
    더불어 건강 더 잘 챙기시고,,,
    최근 내 주위의 좋은 사람들이 하나둘 안 좋은 소식이 들리네유...ㅠ

    2010.01.12 10:55 신고 [ ADDR : EDIT/ DEL : REPLY ]

Languages/Objective-C2008.11.06 14:43
이 글의 원문은 http://en.wikipedia.org/wiki/Objective-C 입니다. 한국 위키피디아에 번역글을 올리고 싶었는데, 올리는 방법을 찾기가 귀찮아서 그냥 여기다 올립니다. 누군가 퍼주실 의향이 있으시다면 올려주시면 고맙겠습니다. :-) 번역에 이상한 부분이 있다면, 역시 지적해주세요. 서너시간만에 낼름한거라 틀린 부분이 있을수도 있습니다. 감사합니다.

- - -

Objective-C는 reflective 객체지향 프로그래밍 언어로서, Smalltalk 스타일의 메시징 메커니즘을 C에 추가한 것이다.

오늘날 이 언어는 Mac OS X와 GNUStep이라는, OpenStep 표준에 기반한 두 환경에서 주로 사용되고 있으며, NextSTEP, OPENSTEP, Cocoa 응용 프레임워크의 주 언어로 쓰인다. 이런 프레임워크가 제공하는 라이브러리를 사용하지 않는 일반적인 Objective-C 프로그램은 gcc가 설치된 시스템이라면 어떤 시스템에서도 컴파일될 수 있다. gcc에는 Objective-C 컴파일러가 포함되어 있는 까닭이다.

역사

1980년대 초, Software Engineering의 일반적 관행은 structured programming의 관습을 따르는 것이었다. 구조화된 프로그래밍 기법은 프로그램을 잘개 쪼개어 작업하기 쉽도록 만드는 것을 돕기 위한 기법이었다. 하지만 풀어야 할 문제 규모가 커짐에 따라 구조화된 프로그래밍 기법의 유용성은 감소하였는데, 더 많은 프로시저가 작성되어야 하다 보니 재사용성이 떨어지고 프로그램 제어 흐름도 복잡해지기 때문이었다.

많은 사람들은 객체지향 프로그래밍 기법이 이 문제에 대한 해답이 될 수 있지 않을까 하고 생각하였다. 사실, Smalltalk라는 프로그래밍 언어는 이미 이러한 Engineering 이슈들을 섭렵하고 있었다. 세상에서 가장 복잡한 시스템들 중 몇몇이 Smalltalk 환경에서 작성되었던 것이다. 하지만 이 언어에도 한가지 문제는 있었으니 가상 머신virtual machine을 사용한다는 것이 그것이었다. Smalltalk의 가상 머신은 이미지image라고 불리는 객체 메모리를 두고 여기에 모든 개발 툴들을 저장해두었다. 그러다보니 메모리 요구량이 그 당시로는 비합리적일 정도로 늘어났고, 느리게 동작할 수 밖에 없었다. 이는 부분적으로는 vm/container 개념을 쓸만하게 지원하는 하드웨어가 없었기 때문이기도 했다.

Objective-C는 Stepstone이라는 회사에서 일하고 있던 Brad Cox와 Tom Love라는 두 연구원이 만들었다. 역시 1980년대 초의 일이다. 이 두 사람은 1981년 ITT의 Programming Technology Center에서 일하던 시절 Smalltalk를 처음 접했다. Cox는 소프트웨어 설계와 프로그래밍에 있어서 재사용성의 문제에 흥미를 느끼게 되었고, Smalltalk같은 프로그래밍 언어가 ITT 시스템 개발자들이 개발을 진행할 강력한 환경을 만드는 데 필요불가결한 요소임을 깨닫게 되었다. Cox는 C 컴파일러를 고쳐 Smalltalk의 기능 일부를 추가하기 시작했고, 곧 그 스스로 "OOPC라고 불렀던 C의 객체지향 버전을 내놓게 되었다. 한편 Love는 1982년에 Schlumberger Research에 채용되었고, Smalltalk-80의 최초 상업적 버전을 써 볼 기회를 가지게 되었다. Smalltalk-80은 이후 그들이 낳은 정신적 자식의 성장에 큰 영향을 끼쳤다.

새로운 언어가 가질 실질적인 파급력을 증명하기 위해, Cox는 기존 툴들에 몇 가지 사소한 변경만 가하면 재사용이 가능한 소프트웨어 컴포넌트를 만들 수 있음을 시연해보였다. 특히 컴포넌트들은 객체를 유연하게 지원해야 했고, 라이브러리들과 함꼐 제공되어야 했으며, 코드 (그리고 그 코드가 필요로 하는 자원들)는 하나의 단일한 플랫폼-중립적 포멧으로 저장될 수 있어야 했다.

결국 Cox와 Love는 Productivity Products International(PPI)이라는 새로운 벤처 회사를 설립한다. 목적은 Objective-C 컴파일러와 강력한 클래스 라이브러리를 결합한 상업 제품을 만들어 파는 것이었다.

1986년에 Cox는 Objective-C의 명세를 담은 책 Object-Oriented Programming, An Evolutionary Approach를 출간한다. 이 책에서 그는 재사용성의 문제가 단순히 언어 차원의 문제가 아님을 지적하였으나, 이후 Objective-C는 종종 다른 언어와 기능적으로 비교되고는 했다.

NeXT를 통해 인기를 얻다

1988년 Steve Jobs는 NeXT라는 회사를 설립한다. NeXT는 StepStone으로부터 Objective-C를 라이센스받았고 NeXTstep 사용자 인터페이스와 그 빌더를 구축하는 데 쓰일 Objective-C 컴파일러와 라이브러리들을 스스로 만들어 내놓았다. NeXT가 내놓은 워크스테이션들이 시장에 강력한 영향력을 행사하지는 못했지만, 그 툴들만큼은 업계에 널리 퍼지게 된다. 결국 NeXT는 하드웨어 제작을 포기하고 소프트웨어 툴들에 집중하기로 방향을 선회하여 NeXTstep (그리고 OpenStep)을 독립적인 사용자 프로그래밍 환경으로 판매하기 시작했다.

이 때 NeXTstep의 free clone을 구축하려는 프로젝트가 GNU에서 시작되었는데, 그 이름은 GNUstep이었다. OpenStep 표준에 바탕을 둔 프로젝트였다. 1992년, Dennis Glatting이 gnu-objc의 런타임을 처음으로 만들었다. 1993년 이후로는 Kresten Krab Thorup이 Denmark에서 대학생시절에 만든 런타임이 널리 쓰였다. Kresten은 1993년부터 1996년까지 NeXT에서 일했다.

1996년에 NeXT는 Apple에 합병된다. Apple은 OpenStep을 기반으로 Mac OS X라는 새로운 운영체제를 내놓는다. 이 안에는 Objective-C와, NeXT의 Objective-C 기반 개발 툴들이 탑재되어 있었다. Project Builder(나중에 Xcode라는 이름으로 바뀐다)라는 개발환경이 제공되었고, Interface Builder라는 인터페이스 설계 툴이 제공되었다. 오늘날 Apple의 Cocoa API 대부분은 OpenStep 인터페이스 객체에 기반을 둔 것이며, 아마 이것이 현존하는 Objective-C 개발 환경 중 가장 널리 쓰이면서도 중요한 것일 것이다.

문법

Objective-C는 C위에 올라가는 아주 얇은 레이어이며, 엄격한(Strict) C 상위집합(superset)이다. 어떤 C 프로그램도 Objective-C 컴파일러로 컴파일할 수 있다는 뜻이다.Objective-C의 문법은 Smalltalk와 C의 문법을 계승하고 있다. 대부분의 문법은 C로부터 가져온 것이지만, 객체지향 기능에 관계된 것들은 Smalltalk-스타일의 메시징 메커니즘을 고려한 것이다.

메시지

Object-C의 문법은 이제 좀 낡아보이는 C 문법에 대안적 요소들을 추가하는 것에 그치지 않고, 객체지향 프로그래밍을 지원한다. Objective-C의 객체지향 프로그래밍 모델은 자족적인 객체에 '메시지'를 보내는 것에 그 근간을 두고 있다. C++이 차용하고 있는 Simula 프로그래밍 모델과는 다른 부분이다. 그리고 이런 차이는 의미적으로 중요하다. 가장 기본적인 차이는, Objective-C는 '메소드를 호출하지 않'으며, '메시지를 보낸다'는 것에 있다. Objective-C에서 메시지의 수신자는 그 메시지를 거부할 수 있다. 둘 중 어느 스타일을 취하건, 장단점은 있다. Simula 스타일의 OOP는 다중 상속을 지원하고 가능한한 컴파일 시간에 바인딩을 하려고 하기 때문에 더 빨리 수행될 수 있다. 또한 virtual 로 선언되지 않은 모든 메소드는 반드시 구현되어 있어야 하며, 설사 virtual로 선언되어 있더라도 구현이 되지 않은 메소드는 호출될 수 없다. Smalltalk 스타일의 OOP는 메시지가 구현되지 않은 상대에게 전송되는 것을 허용하며, 기본적으로 동적 바인딩에 의해 처리된다. 하지만 그렇기 떄문에 때로 느리게 동작하며, 어떤 프로그래머들은 (특히 Simula 진영에 속한 프로그래머들) 디버깅하기가 좀 더 까다롭다고 느낀다. 그런 이유로 Simula 진영 프로그머들은 때로 Objective-C 를 싫어하고, Objective-C 프로그래머들은 Simula 스타일의 언어를 싫어하며, 스스로 상대 진영의 OO 언어는 진정한 OO 언어가 아니며, 심각한 결함이 있다고 느끼기도 한다. (특히 C++은 그런 점에서 가장 자주 공격받곤 하는 언어가 될 것이다. 이에 관해서는 C++ FAQ Lite를 참고하기 바란다.)

메소드 method를 가지고 있는 객체 obj는 메시지 method에 '반응한다'고 일컫는다. C++이라면, obj에 method라는 메시지를 전송하는 코드를 다음과 같이 작성했을 것이다.

obj.method(parameter);

Objective-C에서는 다음과 같이 한다.

[obj method:parameter];

이 메커니즘은 정의된 객체에 메시지가 실행 시간에 전달되도록 한다 - C++과 같은 정적 타입 언어는 현행 표준에 따르면 그런 식으로 동작할 수는 없다. 하지만 C++도 Boost Library가 표준화된다면 ANSI 표준에 따르는 메시징을 지원할 수 있게 될 것이다. Qt는 이 기능을 C++을 비롯한 다른 언어들에게 제공한다(Objective-C 지원은 빈약한 수준이다). 이 기능을 지원하는 많은 클래스들 뿐 아니라 'connect' 함수를 제공하기 때문이다. (동적 타입의 장점에 대해서는 아래의 '동적 타입' 절을 참고하기 바란다.)

Objective-C 에는 메시지 전달과 관계된 몇몇 기능들이 들어 있다. Objective-C 메시지는 반드시 실행될 필요는 없는데, 동적으로 바인딩되기 때문이다. 메시지를 받은 객체가 그 메시지를 구현한 경우 해당 메시지는 실행될 것이지만 그렇지 않은 경우에는 실행되지 않을 것이다. 그렇다 하더라도 코드는 문제없이 컴파일되고 실행된다. 예를 들어, 모든 객체는 awakeFromNib이라는 메시지를 받는다.. 하지만 모든 객체가 이 메시지를 처리하도록 구현되어야만 컴파일이 되는 것은 아니다. 객체에 awakeFromNibg이라는 메시지를 처리하는 부분이 구현되어 있다면 메시지를 받았을 때 그 코드가 실행되겠지만, 그렇지 않은 경우에는 해당 메시지는 무시된다. 또한 메시지는 메시지를 구현한 객체에 보내질 수도 있고, 그 객체가 상속한 상위 클래스에 보내질 수도 있다. 이 메커니즘은 self와 super라는 객체 포인터를 통해 지원된다. 메시지는 nil 객체에도 전달될 수 있다.

인터페이스와 구현

Objective-C는 인터페이스와 클래스 구현이 별도로 선언된 코드 블록에 놓이도록 한다. 관습적으로, 인터페이스는 헤더 파일에 놓이며 구현은 코드 파일에 놓인다. 헤더 파일의 확장자는  .h이고, 구현 파일의 확장자는 통상 .m인데, 통상적인 C 파일들과 유사하다.

인터페이스

클래스의 인터페이스는 보통 헤더 파일 안에 정의된다. 통상적인 관습은 클래스의 이름과 헤더 파일의 이름을 같게 두는 것이다. Class라는 이름의 클래스에 대한 인터페이스는 Class.h에 두도록 한다는 것.

인터페이스 선언은 다음과 같은 형태를 띤다.

@interface classname : superclassname {
    // instance variables
}
+classMethod1;
+(return_type)classMethod2;
+(return_type)classMethod3:(param1_type)parameter_varName;
 
-(return_type)instanceMethod1:(param1_type)param1_varName :(param2_type)param2_varName;
-(return_type)instanceMethod2WithParameter:(param1_type)param1_varName 
andOtherParameter:(param2_type)param2_varName; @end
플러스 기호 +는 클래스 메소드라는 뜻이고, 마이너스 기호 -는 인스턴스 메소드라는 뜻이다. 클래스 메소드는 인스턴스 변수를 접근할 수 없다.

반환값 타입으로는 표준 C 타입, Objective-C 객체에 대한 포인터, 그리고 특별한 종류의 객체에 대한 포인터(NSArray*, NSImage*, NSString* 등)을 사용할 수 있다. 내정(default) 반환값 타입은 Objective-C 타입인 id이다.

메소드의 인자는 콜론 기호 뒤에 인자 타입과 그 이름을 두는 식으로 명시하는데, 타입 이름에는 괄호를 두른다. 어떤 경우에는 (system API를 작성한다거나 하는 경우) 각 인자 이름 앞에 그 의미를 설명하는(descriptive) 텍스트를 두기도 한다.

-(void) setRange:(int)start :(int)end;
-(void) importDocumentWithName:(NSString *)name 
withSpecifiedPreferences:(Preferences *)prefs
beforePage:(int)insertPage;

구현

인터페이스는 클래스의 인터페이스만을 선언하며, 메소드가 어떻게 구현될지는 명시하지 않는다. 실제 코드는 구현(implementation) 안에 들어간다. 구현 파일은 통상 .m의 확장자를 갖는다.

@implementation classname
+classMethod {
    // implementation
}
-instanceMethod {
    // implementation
}
@end

메소드는 인터페이스에 선언된 대로 구현된다. C와 Objective-C의 경우를 비교해 보자.

int function(int i) {
    return square_root(i);
}









-(int)method:(int)i {
    return [self square_root: i];
}

Objective-C는 문법적으로 인자에 대한 pseudo-naming을 지원한다.

-(int)changeColorToRed:(float)red green:(float)green blue:(float)blue
 
[myColor changeColorToRed:5.0 green:2.0 blue:6.0];

이 메소드의 실제표현은 Objective-C 구현체에 따라 달라질 수 있다. myColor가 클래스 Color에 의해 만들어진 것이라면, 인스턴스 메소드 -changeColorWIthRed:green:blue는 내부적으로는 _i_Color_changeColorWIthRed_green_blue와 같은 이름을 갖게 될 수 있다. i는 인스턴스 메소드임을 표시하기 위한 것이고, 그 뒤에 클래스 이름과 메소드 이름을 두었다. 콜론은 전부 _ 기호로 변환되었다. 이 이름을 보면, 인자가 전달되는 순서 또한 메소드 이름의 일부임을 알 수 있다., 메소드에 전달되는 실인자의 순서를 바꿀 수는 없는 것이다. pseudo-naming이라고 한 것은 그래서이다.

하지만 이러한 내부 표현명을 직접적으로 사용할 경우는 거의 없다. 일반적으로 메시지는 Objective-C 런타임 라이브러리에 정의된 함수에 대한 호출로 변환된다. 그러니 링킹 시점에 어떤 메시지가 어떤 메소드를 호출하게 되는지를 알 필요는 없다. 실행 시간까지는 메시지의 리시버 클래스를 알 필요가 없다는 것이다.

객체 생성

일단 Objective-C 클래스가 작성되고 나면, 그 클래스로 객체를 만들 수 있다. 그 과정은 새로운 객체에 대한 메모리를 잡고, 그 메모리를 초기화하는 과정이다. 이 두 과정이 완전히 끝나기 전에는 객체는 완전하게 기능하지 못한다. 통상, 이 과정들은 다음과 같은 한 줄의 코드에 의해 처리된다.

MyObject * o = [[MyObject alloc] init];

alloc 호출은 객체의 인스턴스 변수들을 담기에 충분한 양의  메모리를 잡으며, init 호출은 그 변수들의 값을 특정한 값으로 초가화한다. init 메소드는 통상 다음과 같이 작성된다.

-(id) init {
    self = [super init];
    if (self) {
        ivar1 = value1;
        ivar2 = value2;
        .
        .
        .
    }
    return self;
}

프로토콜

Objective-C는 나중에 NeXT에 의해 확장되어 다중 상속의 개념을 스펙에 포함시키게 되는데, '프로토콜'이 바로 그것이다. 다중 상속은 C++에서처럼 여러 개의 상위 클래스를 상속받는 형태로 제공되기도 하고, Java나 C#에서처럼 '인터페이스'를 계승하는 형태로 지원되기도 한다(이 쪽이 좀 더 인기가 있다). Objective-C는 비정형 프로토콜(informal protocol)이라고 불리는 ad-hoc 프로토콜과, 정형 프로토콜이라고 불리는 컴파일러가 강제하는 프토토콜을 통해 다중 상속을 지원한다.

비정형 프로토콜은 클래스가 구현할 수 있는 메소드의 리스트이다. 그 리스트는 문서(documentation)에 명세되는데, 언어 스펙에는 그 리스트를 표현하기 위한 문법이 없기 때문이다. 비정형 프로토콜은 종종 선택적 메소드(optional method)를 포함하는데, 이 메소드를 구현하면 클래스의 동작이 바뀔 수 있다. 예를 들어, 텍스트 필드 클래스는 그 동작을 다른 객체에 위임(delegation)할 수 있는데, 그 객체는 autocomplete라는 선택가능 메소드를 포함하는 비정형 프로토콜을 구현해야 한다고 해 보자. 텍스트 필드 클래스는 리플렉션을 통해 그 메소드가 해당 객체에 구현되어 있는 것이 분명해지면 해당 메소드를 호출하여 자동 완성 기능이 제공되도록 할 수 있을 것이다.

Object-C에서의 프로토콜 개념은 Java나 C#의 인터페이스와는 좀 다르다. 클래스를 구현할 때, 특정한 프로토콜을 구현하도록 선언하지 않아도 그 프로토콜을 구현해 버릴 수 있기 때문이다. 그 차이는 외부 코드에서는 알아챌 수 없다. 한편 형식 프로토콜은 어떤 구현도 제공하지 않으며, 단순히 특정한 프로토콜을 만족하는 클래스는 해당 프로토콜에 속한 모든 메소드를 구현해야 한다는 것을 강제하기 위해 쓰인다. NeXT/Apple 라이브러리의 경우, 분삭 객체 시스템에 속한 객체가 원격 시스템에서 실행될 수 있는지를 명하하기 위해 프로토콜 개념을 자주 사용하고 있다.

문법은 다음과 같다.

@protocol Locking
- (void)lock;
- (void)unlock;
@end

락을 걸고 푼다는 추상화된 프로토콜이 존재한다는 사실을 명시하고 있다. 클래스를 정의할 때에는 다음과 같이 쓴다.

@interface SomeClass : SomeSuperClass <Locking>
@end

SomeClass에 의해 만들어진 객체들은 Locking 프로토콜에 명시된 두 개의 인스턴스 메소드의 구현을 제공할 것임을 명시하고 있다. 일례로 이런 추상화된 명세법은 구현 계층(hierarchy)이 어떻게 만들어져야하는지를 지정하지 않더라도 플러그인에 요구되는 행위 형태를 기술할 수 있어서 특히 효과적이다.

동적 타입(dynamic typing)

Smalltalk에서와 마찬가지로 Object-C에서는 동적 타입을 사용할 수 있다. 객체는 그 인터페이스에 명시되지 않은 메시지를 수신할 수 있다. 그 결과로 유연성이 향상되는데, 해당 메시지를 "캡처"한 후 또다른 객체에 그 메시지를 보내버릴 수도 있는 것이다. (메시지 수신 객체는 그 메시지에 올바르게 응답을 할 수도 있을 것이고, 아니면 또다른 객체에 메시지를 다시 전송해 버릴 수도 있을 것이다.) 이런 형태의 행위 패턴을 흔히 메시지 전달(message forwarding) 혹은 위임(delegation)이라고 부른다. (위임에 대해서는 아래에 나온다.) 메시지를 포워드(전달)할 수 없을 경우에는 오류 핸들러(error handler)를 통해 처리할 수도 있다. 객체가 메시지를 포워드하지도 않고, 오류 처리를 하지도 않고, 응답하지도 않는 경우에는 런타임 오류가 발생하게 된다.

선택적으로 변수에 정적으로 타입 정보를 부가할수도 있다. 이 정보는 컴파일 시에 검사된다. 아래의 문장들을 보자. 아래쪽으로 갈수록, 더 많은 정적 타입 정보가 부가되어 있다. 실행시간에 이들 문장들은 전부 동일하다. 하지만 부가 정보는 컴파일러로 하여금 전달한 인자의 타입이 맞지 않을 경우 경고를 내 보내줄수 있도록 한다. 첫 번째 문장의 경우, 객체는 어떤 타입이라도 될 수 있다. 두 번째 문장에서는 객체는 반드시 aProtocol 프로토콜을 준수해야만한다. 마지막 문장의 경우에는, 해당 객체는 반드시 NSNumber 클래스의 멤버이어야 한다.

- setMyValue:(id) foo;
- setMyValue:(id <aProtocol>) foo;
- setMyValue:(NSNumber*)foo;

동적 타입은 강력한 기능일 수 있다. 1.5이전의 자바처럼 지네릭(generic) 기능이 빠져있는 정적 타입 언어를 가지고 컨테이너 클래스를 구현하게 되면, 프로그래머는 일반적인 타입의 클래스(자바의  경우에는 Object)에 대한 컨테이너를 만든 다음에 나중에 실제 타입으로 이리저리 캐스팅하는 코드를 작성해야만 한다. 하지만 캐스팅을 사용한 코드는 정적 타입의 원칙을 깨버린다 - Integer 객체를 컨테이너에 저장한다음에 읽을때는 String으로 읽으면, 오류가 발생하게 되는 것이다. 이 문제를 경감하는 한가지 방법은 제네릭 프로그래밍 기법에 의존하는 것이다(C++이라면 템플릿 프로그래밍 기법 같은 것). 하지만 그 경우에는 컨테이너 클래스가 타입 중립적으로 작성되어야만 한다. 동적 타입을 지원하는 언어의 경우에는, 그럴 필요는 없다.

전달(Forwarding)

Objective-C가 아무 객체에나 메시지를 보낼 수 있도록 허용하고 있기 때문에, 객체가 메시지를 받았을 경우 할 수 있는 일도 다양하다. 그 중 하나는 메시지를 다른 객체로 전달하는 것이다. 이 기법을 사용해 옵저버 패턴이나 프록시 패턴 같은 디자인 패턴을 간단히 구현할 수 있다.

Objective-C 런타임은 Object 클래스에 몇 가지 메소드들을 정의해놓고 있다.

전달 메소드들:

- (retval_t) forward:(SEL) sel :(arglist_t) args; // with GCC
- (id) forward:(SEL) sel :(marg_list) args; // with NeXT/Apple systems

액션 메소드들:

- (retval_t) performv:(SEL) sel :(arglist_t) args;  // with GCC
- (id) performv:(SEL) sel :(marg_list) args; // with NeXT/Apple systems

메시지를 전달하고 싶은 객체는 전달 메소드를 오버라이드 하여 구현하면 된다. 액션 메소드 peformv::는 오버라이드 할 필요 없는데, 이 메소드는 단순히 셀렉터(selector)와 인자를 가지고 메소드를 실행하는 역할만 하기 때문이다.

예제
다음은 전달(forwarding)의 기본기를 보여주는 예제 프로그램이다.

Forwarder.h
#import <objc/Object.h>
 
@interface Forwarder : Object
{
    id recipient; //The object we want to forward the message to. 
}
 
//Accessor methods
- (id) recipient;
- (id) setRecipient:(id) _recipient; 
 
@end
Forwarder.m
#import "Forwarder.h"
 
@implementation Forwarder
 
- (retval_t) forward: (SEL) sel : (arglist_t) args
{
    /*
     * Check whether the recipient actually responds to the message. 
     * This may or may not be desirable, for example, if a recipient
     * in turn does not respond to the message, it might do forwarding
     * itself.
     */
    if([recipient respondsTo:sel]) 
       return [recipient performv: sel : args];
    else
       return [self error:"Recipient does not respond"];
}
 
- (id) setRecipient: (id) _recipient
{
    recipient = _recipient;
    return self;
}
 
- (id) recipient
{
    return recipient;
}
 @end
Recipient.h
#import <objc/Object.h>
 
// A simple Recipient object.
@interface Recipient : Object
- (id) hello;
@end
Recipient.m
#import "Recipient.h"
 
@implementation Recipient
 
- (id) hello
{
    printf("Recipient says hello!\n");
 
    return self;
}
 
@end
main.m
#import "Forwarder.h"
#import "Recipient.h"
 
int
main(void)
{
    Forwarder *forwarder = [Forwarder new];
    Recipient *recipient = [Recipient new];
 
    [forwarder setRecipient:recipient]; //Set the recipient. 
    /* 
     * Observe forwarder does not respond to a hello message! It will
     * be forwarded. All unrecognized methods will be forwarded to
     * the recipient 
     * (if the recipient responds to them, as written in the Forwarder)
     */
    [forwarder hello]; 
 
    return 0;
}

노트
이 프로그램을 컴파일하면 컴파일러가 다음과 같은 메시지를 보여줄 것이다.

$ gcc -x objective-c -Wno-import Forwarder.m Recipient.m main.m -lobjc
main.m: In function `main':
main.m:12: warning: `Forwarder' does not respond to `hello'
$

Forwarder가 hello 메시지에 응답하지 않는다는 경고 메시지이다. 어떤 환경에서는 저런 경고 메시지를 통해 좀 더 쉽게 오류를 찾아낼 수 있다. 하지만 지금 이 환경에서는 이 경고 메시지는 무시해도 좋다. 전달 기능을 구현했기 때문이다. 그러니 프로그램을 실행해 보면 다음과 같은 메시지를 보게 될 것이다.

$ ./a.out
Recipient says hello!

[다음 글에 계속]
신고
Posted by 이병준

소중한 의견, 감사합니다. ^^