LazarusCompleteGuide:6.1
RTL (프리 파스칼 런타임 라이브러리)
RTL의 클래스는 가장 기본적인 클래스이다: Class 유닛에 위치하며 모든 것의 기반을 형성한다. 사실 이 유닛은 매우 소수의 클래스를 포함하는데, 물론 모두 비시각적이다. System, SysUtils, Strings 유닛(그리고 다른 많은 유닛)에 포함된 대부분 루틴은 철저히 절차적이며, 여기서 논하게 될 클래스를 이해하는 데에 그다지 중요하지 않다. RTL에 관한 전체 참조내용은 C&L 출판회사에서 펴낸 Michael Van Canneyt 저자의 Free Pascal 2, ISBN 978-3-936546-53-8에서 찾을 수 있다.
RTL의 기반 클래스는 아래와 같다:
- TPersistent
해당 클래스는 RTTI를 (런타임 타입 정보) 이용하는 모든 것에 대한 기반 클래스로서 사용된다. 클래스를 라자루스 오브젝트 인스펙터에서 조작할 필요가 있는 경우, TPersistent의 자손이어야 한다. - TComponent
해당 클래스는 TPersistent 의 자손으로, 이름, 소유권(ownership), 스트리밍(streaming)의 개념을 (아래 설명) TPersistent 에 추가한다. 클래스를 라자루스 폼 디자이너(Form Designer)에서 조작할 필요가 있는 경우, TComponent의 자손이어야 한다. - TStream
해당 클래스는 입/출력의 정의, 또는 좀 더 일반적으로 메모리 내부나 디스크 상에 데이터를 이동시키는 데에 사용된다. 두 가지 중요한 자손으로는 TFileStream과 (디스크 상의 데이터 의미) TMemoryStream이 (메모리 내 데이터 의미) 있다. - TCollection
해당 클래스는 컴패니언(companion) 클래스 TCollectionItem (TPersistent 자손)과 함께 사용 시 동일하게 구조화된 항목의 집합체를 관리할 수 있는데, 그러한 항목으로는 격자에 있는 열(columns in a grid), 액션리스트 내 액션 등 다수가 있다. - TList
기타 클래스의 리스트를 관리하는 데에 사용되는 일반 리스트 클래스. - TStrings
문자열의 리스트를 관리 및 조작하도록 설계된 클래스. 추상 클래스로서, 많은 용도로 사용되는 TStringList 를 자손으로 가진다.
위의 6가지 클래스는 라자루스에서 실행되는 모든 프로그래밍의 기반을 형성하며, 그림 6.1과 같은 클래스 계층구조로 표시할 수 있다.
스트리밍과 RTTI
Classes 유닛은 스트리밍 시스템을 정의한다. 스트리밍 시스템은 라자루스 IDE에서 폼에 있는 컴포넌트의 모든 published 프로퍼티를 스트림으로 (주로 파일, .lfm 파일) 저장하도록 허용하기 위해 설계되었다. published 프로퍼티는 오브젝트 인스펙터에서 조작 가능한 프로퍼티를 의미한다. 이 파일은 런타임 시 모든 프로퍼티를 복원할 수 있을 만큼의 충분한 정보를 포함하여 스트림 데이터로부터 폼을 재구성할 수 있도록 해준다. 라자루스가 디스크에서 폼 파일을 열 때도 같은 일이 발생한다.
스트리밍 시스템은 Classes 유닛에서 정의되기 때문에 어떤 객체든 그 상태를 복원시키길 원하는 프로그래머라면 누구든 이용할 수 있다. TComponent로부터 객체를 파생하고(descend), 객체의 현재 상태를 저장하기 위해 스트리밍 시스템을 사용하기만 하면 된다. 스트림 내 데이터는 이후 디스크로 저장하여 (어쩌면 데이터베이스에) 객체의 상태를 복구시킬 때 검색할 수 있다.
스트리밍 시스템은 published 프로퍼티의 유무에 좌우된다. 클래스의 Published 섹션에서 정의된 프로퍼티라면, 컴파일러가 메타데이터, Run Time Type Information (RTTI)를 생성한다. 이 정보를 사용해 published 프로퍼티의 값을 얻거나 설정할 수 있다 (이 시스템을 .NET과 Java에서 Introspection이라 부른다). RTTI는 스트림에 모든 published 프로퍼티를 (주로 .lfm 파일로) 쓸 때 스트리밍 시스템에 의해 사용되며 그에 따라 런타임에서 스트림의 데이터로부터 폼을 재구성할 때 사용한다.
RTTI는 애플리케이션 실행 파일에 저장되므로 디스크와 메모리 공간을 많이 차지하는데, 불필요한 일일지도 모른다. 때문에 클래스에는 자동으로 프로퍼티를 publish하는 기능이 없다. 클래스가 자신과 그 모든 자손에 대해 published 프로퍼티의 이용을 활성화시키려면 특수 컴파일러 스위치 {$M+}를 이용해 클래스를 컴파일해야 한다. 해당 스위치로 컴파일되지 않은 클래스에서 published 프로퍼티를 이용하는 경우 컴파일러 오류를 야기한다.
TPersistent 클래스는 이 컴파일러 스위치로 컴파일되었기 때문에 TPersistent 의 모든 자손들은 published 프로퍼티를 이용할 수 있다. 라자루스 IDE는 오브젝트 인스펙터에서 published 프로퍼티를 조작할 때 RTTI를 사용하므로 IDE에서 조작해야 하는 클래스라면 TPersistent 의 자손이어야 한다는 규칙을 따른다.
런타임 시 TForm 인스턴스가 생성되면 스트리밍 시스템은 인스턴스가 포함하는 모든 컨트롤을 (비시각적 컨트롤 포함) 생성하고, 해당 클래스의 인스턴스로서 메모리에 존재한다. 폼이 더 이상 필요 없다면 (예: 사용자가 닫거나 애플리케이션이 중지 시) 폼 인스턴스는 파괴되어 메모리에서 제거된다. 물론 폼의 모든 컴포넌트들도 뒤따라 파괴되어야 하는데, 그렇지 않을 경우 메모리 누수(memory leak)가 발생할 것이다.
TComponent 클래스는 이러한 일이 확실히 일어나도록 보장하기 위해 도입되었다ㅡ따라서 폼을 설계하는 프로그래머는 자신이 사용하는 컴포넌트가 모두 올바르게 파괴되었는지 염려할 필요가 없다. TComponent 는 다른 TComponents (자식들)도 소유할 수 있다. 소유자 TComponent 가 메모리에서 제거되면 소유자는 자신이 소유한 다른 모든 컴포넌트도 자동으로 메모리에서 제거하며, 그래야만 스스로를 메모리에서 제거할 수 있을 것이다.
TForm 과 TDatamodule 은 (비시각적 컴포넌트를 위한 보이지 않는 상자) 모두 TComponent 의 자손이다. 이 상자에 드롭된 컨트롤도 모두 TComponent 의 자손이며, TForm (또는 TDatemodule) 인스턴스의 소유로 자동 등록된다. 폼이 (또는 데이터모듈이) 닫히고 메모리에서 제거되면, 그들이 소유하는 모든 컨트롤도 메모리에서 자동으로 제거된다. 폼에 드롭되는 비시각적 컴포넌트도 마찬가지다.
이러한 자동 메모리 관리 기능은 사용자가 라자루스 IDE 내 폼에 드롭하는 객체라면 모두 TComponent 의 자손이 되는 이유를 설명한다. 이는 스트리밍 시스템이 TComponent 자손의 스트리밍만 허용하는 이유이기도 한데, 스트리밍 시스템에 의해 자동으로 생성되는 클래스 인스턴스는 모두 자동으로 제거되어야 하기 때문이다.
TComponent와 클래스 인스턴스의 명명
모든 컴포넌트는 Name 프로퍼티를 가진다. Name 프로퍼티는 수치형(numerical) Tag 프로퍼티까지도 정의하는 TComponent 에서 선언된다:
TComponent = class(TPersistent,IUnknown,IInterfaceComponentReference)
private
...
public
...
published
property Name: TComponentName read FName write SetName stored False;
property Tag: Longint read FTag write FTag default 0;
end;
이는 폼의 Name을 왜 유효한 파스칼 식별자로 제한하는지 즉시 설명해준다. 폼 선언은 컴파일러에 의해 파싱(parse) 및 컴파일러가 이루어지므로, 폼으로 드롭되는 모든 컴포넌트의 이름은 유효한 오브젝트 파스칼 식별자여야 한다.
스트리밍 시스템은 폼의 선언에서 Button1 필드가 실제로 'Button1'이라는 이름의 TButton 인스턴스를 포함하도록 보장하는데, 이는 컴포넌트의 Name 프로퍼티가 특별하게 처리되기 때문이다.
스트리밍 시스템은 런타임 시 폼을 재생성할 때 TButton 인스턴스를 생성하고 그 Name을 'Button1'으로 설정한다. 그리고 나서 TForm1 클래스의 RTTI 정보를 확인하고, TButton 타입의 필드와 ‘Button1’ 이름을 검색한다. 이후에 필드의 값을 방금 생성한 TBUtton 인스턴스로 설정한다. 이것이 가능한 이유는, 폼에 드롭된 모든 컴포넌트는 폼의 published 섹션에서 필드로서 선언되기 때문이다 (어떤 TPersistent 클래스든 첫 번째 섹션의 기본 값은 Published이다).
물론 이 모든 과정은 투명하게 진행된다. 정말로 중요한 것은 프로그래머가 폼에 컴포넌트를 드롭한 후 Name을 부여하는 순간 폼의 필드로 선언되기 때문에 동일한 이름을 이용해 폼의 코드로 접근할 수 있다는 점이다.