Remote Messaging
OSXDEV
다른 프로그래밍 언어들과 마찬가지로, Objective-C는 처음부터 단일 어드레스 공간에서 동작하는 단일 프로세스 프로그램을 위해 디자인되었다.
그렇지만, 상대적으로 self-contained 유닛들 간에 런타임시에 발생하는 메세지를 통한 커뮤니케이션이 발생하는 오브젝트-지향의 모델은, 프로세스간 통신을 위해 잘 준비된 것 같다. 서로 다른 주소 공간(즉, 다른 태스크) 에 상주하는 오브젝트들 또는 같은 태스크 내에서 다른 쓰레드 동작들 간에 Objective-C 메세지를 보내는 것을 상상하는 것은 어렵지 않다.
예를 들어, 전형적인 서버-클라이언트 상호작용에서, 클라이언트 태스크는 서버에 있는 지정된 오브젝트로 요청을 보낼 수 있고, 서버는 notification과 다른 정보를 수신할 클라이언트 오브젝트를 지정할 수 있다.
혹은 사용자의 요청을 수행하기 위해 많은 계산을 해야 하는 인터렉티브 어플리케이션을 상상해 보자. 그냥 단순히사용자에게 계산하는 동안 기다리라는 다이얼로그만 띄워놓을 수도 있지만, 하부 태스크로 그 처리작업을 분리시키고 어플리케이션의 주 파트는 사용자의 입력을 받을 수 있도록 할 수도 있다. 두 테스크의 오브젝트들간의 통신은 Objective-C 메세지를 통한다.
유사하게, 몇개의 분산 프로세스들이 하나의 도큐멘트를 합동으로 편집할 수도 있다.도큐멘트 안의 각각의 타입의 데이터 편집을 위해서는 서로 다른 편집 툴이 필요할 수도 있다. 하나의 태스크는 스크린상의 통합된 사용자 인터페이스를 제공하고 다양한 편집 툴들과 해당 명령을 정렬하는 일을 맡는다. Objective-C 메세지가 유저 인터페이스와 툴, 그리고 툴들 간 통신의 나룻배 역할을 하기 위해서 각각의 합동 태스크는 Objective-C로 작성되어야 할 것이다.
목차 |
[편집] Distributed Objects
Objective-C에서의 리모트 메세징은 서로 다른 주소 공간에 있는 오브젝트들 간의 연결을 만들고, 원격 어드레스 공간에 있는 오브젝트를 위한 메세지를 알아내고, 하나의 어드레스 공간에서 다른 곳으로 데이터를 전송하기 위해서 런타임 시스템을 필요로 한다. 이것은 또한 두개의 태스크 사이의 분리된 스케쥴을 중계한다; 원격에 있는 리시버가 응답을 할 수 있는 상황에 올 때까지 메세지를 가지고 있어야 한다.
코코아는 런타임 시스템에 일종의 익스텐션으로 distributed objects 아키텍쳐를 포함한다. distributed objects를 사용하면, 다른 테스크에 있는 오브젝트로 Objective-C메세지를 보내거나 동일 테스크의 다른 쓰레드에서 동작하는 메세지를 가질 수 있다. (리모트 메세지가 동일한 태스크내의 두개 쓰레드간에 보내지면, 쓰레드는 다른 태스크의 쓰레드와 완전히 동일하게 취급된다.) 코코아의 distributed objects 시스템은 런타임 시스템위에 올라가 있다;그러므로 코코아 오브젝트의 기본적인 동작을 바꾸지는 않는다.
원격 메세지를 보내려면, 어플리케이션은 먼저 원격에 있는 수신자와 연결을 해야 한다. 연결을 하면 어플리케이션 자신의 어드레스 공간에 원격 오브젝트의 프락시를 만들게 된다. 이 후 이 프락시를 통해 원격 오브젝트와 통신한다. 프락시는 원격 오브젝트는 원격 오브젝트의 아이덴티티라고 가정한다. 즉, 그 자신은 아이덴티티가 없다. 어플리케이션은 프락시를 원격 오브젝트로 취급할 수 있다. 즉, 대부분의 경우 원격 오브젝트 그 자체이다.
그림 12-6은 원격 메세징을 설명한다. 오브젝트 A가 프락시를 통해 오브젝트B와 통신하는 중 B로의 메세지들이 B가 준비될 때까지 기다린다.
그림 12-6 원격 메세지
sender와 receiver는 서로 다른 태스크에 있으며 독립적으로 스케쥴된다. 그러므로 sender가 전송할 수 있을 때 receiver가 수신할 수 있는 상태라는 보장이 없다. 그러므로, 도착한 메세지들은 큐에 저장되고 수신측 어플리케이션이 편한대로 접수된다.
프락시는 원격 오브젝트를 대신하거나 그 클래스에 접근할 수 없다. 이것은 오브젝트의 복사본이 아니라 손쉬운 대체물이다. 말하자면, 이것은 투명하다; 이것은 수신하는 메세지들을 원격에 있는 수신자에게 전달 하고 프로세스간 통신을 관리할 뿐이다. 주된 기능은 실제로 존재하지 않아야 하는 오브젝트에 대한 로컬 주소를 제공하는 것이다. 그렇지만 프락시가 완전 투명한 것은 아니다. 예를 들어 프락시는 오브젝트의 인스턴스 변수를 직접적으로 set하고 get하도록 하지는 않는다.
원격 리시버는 전형적으로 익명이다. 클래스는 원격 어플리케이션안에 숨겨져 있다. 전송측 어플리케이션은 그 어플리케이션이 어떻게 디자인 되었고 어떤 클래스를 사용하는 지 알 필요가 없다. 같은 클래스를 사용할 필요가 없는 것이다. 알아야 할 것은 리모트 오브젝트가 어떤 메세지에 응답하는가 이다.
이 때문에, 원격 메세지를 받으려고 지정된 오브젝트는 자신의 인터페이스를 포멀 프로토콜로 알려야 한다. 송신과 수신 어플리케이션 모두 프로토콜을 선언한다 - 둘 다 같은 프로토콜 정의를 import한다. 수신 어플리케이션은 원격 오브젝트가 프로토콜을 따라야 하기 때문에 선언한다. 전송 어플리케이션은 컴파일러에게 보낼 메세지에 대해 알리기 위해서, 혹은 이것이 conformsToProtocol: 메소드와 @protocol()지시어를 사용하여 원격의 수신자를 테스트 해야 하기 때문에 선언한다. 전송 어플리케이션은 프로토콜의 메소드를 구현할 필요가 없다; 원격 수신자에게 메세지를 처음 보내기 위한 프로토콜만 정의하면 된다.
distributed object의 구조는, NSProxy와 NSConnection 클래스를 포함하여 Foundation 프레임워크 레퍼런스와 Distributed Objects Programming Topics에 문서화 되어있다.
[편집] Language Support
리모트 메세징은 프로그램 디자인에 있어서 수많은 흥미거리를 제공해 줬을 뿐만 아니라, Objective-C 언어에 몇가지 재미있는 이슈를 만들어 줬다. 대부분의 이슈는 효과적인 리모트 메세징과 관련된 것이며 두개의 테스크가 서로 통신하는 중에 유지해야 하는 독립 수준에 관한 것이다.
그러므로 프로그래머들은 원격 메세지의 목적에 대한 명확한 명령어를 제공해야 한다. Objective-C는 포멀 프로토콜 안에서 메소드를 선언할 때 사용할 수 있는 여섯가지 타입의 수식어(qualifier)를 정의한다.
oneway
in
out
inout
bycopy
byref
이 수정자들은 포멀 프로토콜에만 한정된다; 클래스와 카테고리 선언에는 사용될 수 없다. 그러나 만약 클래스나 카테고리가 프로토콜을 적용한다면, 그 프로토콜 메소드의 구현에서는 메소드를 선언에 사용된 것과 동일한 수정자를 사용할 수 있다.
다음 섹션은 그 수정자들이 어떻게 사용되는 지를 설명한다.
[편집] Synchronous and Asynchronous Messages
먼저 간단한 리턴값을 가지는 메소드를 생각해 보자.
- (BOOL)canDance
동일한 어플리케이션에서 canDance 메세지가 receiver로 보내졌다면, 메소드가 발생하고 리턴값은 sender에게 바로 제공된다. 그러나 receiver가 원격 어플리케이션안에 있다면, 두개의 하부 메세지가 필요하다 - 하나의 메세지는 메소드를 발생시킬 원격 오브젝트를 구하기 위한 것이고, 다른 하나는 원격 계산의 결과를 반환하기 위한 것이다. 다음 그림에서 설명하고 있다.
그림 12-7 반환 메세지
대부분의 원격 메세지들은 그 밑바닥에 이와 같은 양방향(혹은 "reound trip") 원격 프로시져 호출(RPCs)을 가지고 있다. 송신 어플리케이션은 수신 어플리케이션이 메소드를 발생시키기를 기다리고, 그 처리를 끝내고, 마무리 된 indication을 요청된 리턴 정보값과 함께 반환한다. receiver가 끝나기를 기다리는 것은, 리턴되는 정보값이 없더라도, 통신하고 있는 두개의 어플리케이션 양쪽 모두를 "in sync"로 유지하는 이점이 있다. 이런 이유에서, reound-trip 메세지들은 종종 synchronous 메세지로 불린다. 싱크로노스 메세지가 디폴트이다.
그러나, 응답을 기다리는 것이 항상 필수적이거나 좋은 아이디어 인 것은 아니다. 가끔은 그냥 메세지를 발송하고 리턴하여 리시버가 가용할 때 그 태스크를 받도록 하는 것으로 충분할 수 있다. 그동안 sender는 다른 일을 할 수 있다. Objective-C는 메소드가 asynchronous 메세지에만 사용된다는 것을 나타내는oneway라는 리턴 타입 수정자를 제공한다:
- (oneway void)waltzAtWill;
oneway가 타입 수식어이고(const 처럼), oneway float이나 oneway id처럼 특정 타입 이름과 조합하여 사용할 수 있지만, 의미를 가지는 유일하 조합은 oneway void이다. 어싱크로노스 메세지는 유효한 리턴 값을 가지지 못한다.
[편집] Pointer Arguments
다음으로, 포인터 매개변수를 가지는 메소드를 생각해 보자. 포인터는 수신측에 참조에 의해 정보를 넘겨주는 데 사용할 수 있다. 메소드가 발생되면, 넘겨진 주소값에 어떤 것이 저장되어 있는지를 확인한다
- setTune:(struct tune *)aSong
{
tune = *aSong;
...
}
동일한 종류의 매개변수가 참조로부터 정보를 받아오기 위해 사용된다. 메소드는 포인터를 이용해 메세지에서 요구되는 정보를 위치시킬 곳을 찾는다.
- getTune:(struct tune *)theSong
{
...
*theSong = tune;
}
포인터가 사용되는 방식의 차이가 원격 메세지가 실행되는 방식의 차이를 가져온다. 어떠한 경우라도 포인터가 변경되지 않고 원격 오브젝트로 넘겨지지는 않는다; 이것은 sender의 주소 공간에서의 메모리 위치를 가리키는 것이지, 원격 오브젝트의 주소 공간에서는 의미를 가지지 못한다. 리모트 메세징을 위한 런타임 시스템은 장막 뒤에서 몇가지 조정을 해 줘야 한다.
변수가 참조를 통해 정보를 전달하려 한다면, 런타임 시스템은 그 포인터에 대한 참조를 없애고, 그것이 가리키는 값을 원격 어플리케이션으로 실어보내고, 어플리케이션의 로컬 어드레스에 그 값을 저장하고, 그 주소를 원격 수신자에게 전한다.
만약 그 반면에, 포인터가 참조에 의해 정보를 리턴하는 데 사용되었다면, 그 포인터가 가리키는 값은 다른 어플리케이션으로 보내질 필요가 없다. 그 대신, 다른 어플리케이션으로부터의 값이 반환되어야 하며 포인터에 의해 지정된 위치에 쓰여져야 한다.
첫번째 경우에서, 정보는 round trip의 첫번째 구간에 념겨진다. 두번째 경우에서, 정보는 round trip의 두번째 구간에서 리턴된다. 이러한 케이스들이 아주 다른 원격 메세징을 위한 런타임 시스템의 액션들을 초래하기 때문에, Objective-C는 프로그래머의 의도를 명확하게 할 수 있는 타입 수정자를 제공한다.
in 수정자는 메세지를 통해 정보가 넘겨지는 것을 나타낸다.
- setTune:(in struct tune *)aSong;
out 수정자는 변수가 참조를 통해 정보를 리턴하는 데 사용되었다는 것을 나타낸다.
- getTune:(out struct tune *)theSong;
세번째 수정자인, inout은 변수가 정보를 제공하고 다시 되받는 양쪽 모두에 사용되는 것을 나타낸다.
- adjustTune:(inout struct tune *)aSong;
코코아의 distrubuted object 시스템은 in이 디폴트가 되는 const 선언을 제외하고는 모든 포인터 변수에 inout을 디폴트 수정자로 가진다. inout은 가장 안전하다고 가정하지만, 양쪽 모두로 정보를 전달해야 하기 때문에 가장 시간을 많이 잡아먹는 것이기도 하다. 값으로 (포인터가 아닌) 변수가 넘겨져도 의미가 통하는 유일한 수정자는 in이다. in이 어떤 종류의 변수든 사용할 수 있는 것과 달리, out과 inout은 포인터를 사용해야만 한다.
C에서, 포인터는 이따금씩 컴포지트 값을 나타내기 위해 사용된다. 예를 들어, 스트링은 캐릭터 포인터 (char *)로 나타내어진다. 몇단계의 간접적 표기법과 구현이 있지만, 개념적으로는 그렇지 않다. 개념적으로, 스트링은 그 자신이 스스로 다른 것에 대한 포인터가 아니다.
이와같이, distributed objects 시스템은 자동적으로 포인터의 참조를 해제하고 그것이 가리키는 것이 무엇이든 그 값을 넘긴다. 그러므로, out과 inout 수정자는 간단한 캐릭터 포인터로는 이용할 수 없다. 원격 메세지가 참조에 의해 스트링을 넘기거나 리턴하려면 부가적인 몇 단계의 간접표기가 필요하다:
- getTuneTitle:(out char **)theTitle;
오브젝트에 대해서도 통한다.
- adjustRectangle:(inout Rectangle **)theRect;
이러한 법칙은 컴파일러가 아닌 런타임시에 강요된다. Proxies and Copies 마지막으로, 변수로 오브젝트를 가지는 메소드를 생각해 보자.
- danceWith:(id)aPartner;
danceWith메세지는 receiver에 id 오브젝트를 넘긴다. sender와 receiver가 동일한 어플리케이션이라면, 그 둘은 동일한 aPartner오브젝트를 참조할 것이다.
이는 receiver가 원격 어플리케이션에 있더라도, receiver가 프락시를 통해 오브젝트를 참조해야 한다는 것만을 제외하고는 같다 (오브젝트가 같은 어드레스 공간에 있지 않으므로). dnaceWith:가 원격 receiver로 전달하는 포인터는 실제로는 프락시에 대한 포인터이다. 프락시로 보내진 메세지는 실제 오브젝트로의 연결을 타고 넘겨지고 모든 리턴되는 오브젝트는 원격 어플리케이션으로 반환되어진다.
가끔씩 프락시가 불필요하게 비효율적이고, 차라리 원격 프로세스로 오브젝트의 복사본을 보내서 그 자신의 주소 공간 내에서 직접 인터렉트 하는 것이 나을 때가 있다. 이러한 경우 프로그래머에게 방법을 주기 위해, Objective-C는 bycopy 타입 수정자를 제공한다:
- danceWith:(bycopy id)aClone;
bycopy는 리턴값에도 사용될 수 있다.
- (bycopy)dancer;
이는 유사하게 out과 함께 사용하여 참조에 의해 리턴되는 오브젝트가 프락시의 형태가 아닌 복사되어 오도록 지정할 수 있다:
- getDancer:(bycopy out id *)theDancer;
| NOTE: 오브젝트의 복사본이 다른 어플리케이션으로 넘겨질 때는, 익명이 될 수는 없다. 오브젝트를 수신하는 어플리케이션은 그 주소공간에 로드되는 오브젝트의 클래스를 가지고 있어야 한다. |
bycopy 는 특정 클래스들- 예를 들어, 다른 오브젝트들의 컬렉션을 가지고 있는 클래스들 - 에 대해 많은 의미를 가지고 있다. 그 클래스들은 일상적인 레퍼런스를 대신해 복사본이 원격 receiver로 보내지도록 쓰여졌다. 이러한 비헤비어들은 byref로 오버라이드 할 수 있지만, 메소드로 넘겨지는 오브젝트나 메소드로부터 리턴된 오브젝트를 지정하는 것은 참조로 넘겨지거나 참조로 리턴되어야 한다. 참조를 넘기는 것은 Objective-C 오브젝트에게는 광범위한 주류 비헤비어이기 때문에, byref 키워드를 사용할 일은 거의 없을 것이다.
다이나믹하게 타입된 id이거나 클래스 이름으로 정적으로 타입된 것에 상관없이 수정을 위해 bycopy나 byref를 사용할 수 있는 유일한 타입은 오브젝트이다.
bycopy와 byref 가 클래스 내부나 카테고리 선언에 사용될 수 없지만, 포멀 프로토콜안에서는 사용가능하다. 예를 들어, 포멀 프로토콜 foo를 다음과 같이 쓸 수 있다:
@Protocol foo - (bycopy)array; @end
클래스 혹은 카테고리는 당신의 foo프로토콜을 도입할 수 있다. 이는 프로토콜에 있는 메소드에 의해 오브젝트가 어떻게 넘겨지고 어떻게 리턴되는 지에 대한 힌트를 제공함으로서 당신이 직접 프로토콜을 만들 수 있도록 한다.
| 번역자 | 사용자:LingoStar |
| 원본문서링크 | http://developer.apple.com/DOCUMENTATION/Cocoa/Conceptual/ObjectiveC/Articles/chapter_13_section_8.html (Last Updated - 2007-10-29) |






