이 글은 Marco Cantu가 작성한 엠바카데로 블로그 글을 번역(및 일부의역)한 것입니다


 

 

델파이 관리 레코드(Managed Record)란?

델파이 레코드는 모든 데이터 유형의 필드를 가질 수 있습니다. 레코드에 숫자, 열거형 등 일반(관리되지 않는) 필드가 있는 경우라면 컴파일러가 별로 할 일이 없습니다. 레코드 생성, 폐기는 메모리를 할당하고, 해제하는 것 뿐입니다. (참고로, 델파이는 레코드를 0-초기화(Ordinal 타입의 기본값을 0으로 초기화) 하지 않음을 주의하세요.)

레코드에 컴파일러에 의해 관리되는 타입의 필드(예> 문자열, 인터페이스)를 가지는 경우, 컴파일러는 초기화와 종료를 관리하는 코드를 삽입합니다. 예를 들어, 문자열은 참조 카운트 기반이므로 레코드가 범위를 벗어나면 레코드 내부의 문자열 참조 카운트를 줄여 문자열의 메모리를 해제합니다. 따라서 코드에서 관리 레코드를 사용하는 경우, 컴파일러는 자동으로 해당 코드 주변에 try finally 블록을 추가해, 예외 발생 시에도 데이터가 제거 되도록 합니다. 이미 오래전 부터 델파이 언어는 관리 레코드를 지원했습니다.

 

레코드의 초기화(Initialize) 및 종료(Finalize) 연산자

10.4의 델파이 레코드 타입은 초기화 및 종료 코드를 직접 작성할 수 있게되었습니다. 관리 레코드에 대해 컴파일러의 기본 동작을 재정의 할 수 있습니다. 이는 새로운 연산자를 추가하는 방식으로 진행됩니다.(초기화, 종료 중 하나만 작성도 가능)
 

다음은 간단한 예제입니다.

type
  TMyRecord = record
    Value: Integer;
    class operator Initialize (out Dest: TMyRecord);
    class operator Finalize(var Dest: TMyRecord);
  end;

두개의 클래스 메소드 코드 작성이 필요합니다.
예를 들어 연산자 실행에 대한 로그를 기록하거나 레코드의 값을 초기화할 수 있습니다. 또한 메모리 주소 참조 로그를 통해 어떤 레코드의 연산자가 수행되는지도 확인할 수 있습니다.

class operator TMyRecord.Initialize (out Dest: TMyRecord);
begin
  Dest.Value := 10;
  Log('created' + IntToHex (Integer(Pointer(@Dest)))));
end;

class operator TMyRecord.Finalize(var Dest: TMyRecord);
begin
  Log('destroyed' + IntToHex (Integer(Pointer(@Dest)))));
end;

이 생성자 메커니즘과 이전 레코드의 가장 큰 차이점은 자동 호출입니다. 아래 코드와 같이 작성 시 초기화와 종료 연산자를 모두 호출합니다. 컴파일러는 관리 레코드 인스턴스 관리를 위해 내부적으로 try-finally 블록을 생성합니다.

procedure LocalVarTest;
var
  my1: TMyRecord;
begin
  Log (my1.Value.ToString);
end;

위 코드 실행시 다음과 같은 로그를 확인할 수 있습니다.

created 0019F2A8
10
destroyed 0019F2A8

다른 시나리오는 다음과 같은 인라인 변수 사용입니다. 인라인 변수도 동일한 로그를 확인하게 됩니다.

begin
  var t: TMyRecord;
  Log(t.Value.ToString);

 

할당(Assign) 연산자

대입연산자(:=)를 통한 할당은 레코드 필드의 모든 데이터를 그대로 복사합니다. 사용자정의 데이터 필드와 사용자정의 초기화 코드가 있는 경우 이 동작을 변경해야 할 수 있습니다. 이러한 이유로 사용자정의 관리 레코드의 경우 할당 연산자도 정의할 수 있습니다. 새 연산자는 Assign으로 정의하고, := 구문으로 호출합니다.

  class operator Assign (var Dest: TMyRecord; const [ref] Src: TMyRecord);

연산자 정의는 첫 번째 파라미터를 참조 파라미터(var)로, 두 번째 파라미터를 참조 전달 상수(const [ref])로 사용하는 것을 포함해 매우 엄격한 규칙을 따라야 합니다. 그렇지 않은 경우 컴파일러는 다음과 같은 오류 메시지를 표시합니다.

[dcc32 Error] E2617 First parameter of Assign operator must be a var parameter of the container type
[dcc32 Hint] H2618 Second parameter of Assign operator must be a const[Ref] or var parameter of the container type

다음의 코드는 Assign 연산자를 호출합니다.

var
  my1, my2: TMyRecord;
begin
  my1.Value := 22;
  my2 := my1;

다음과 같이 로그가 표시됩니다.(레코드에 시퀀스 번호 추가)

created 5 0019F2A0
created 6 0019F298
5 copied to 6
destroyed 6 0019F298
destroyed 50019F2A0

소멸 순서는 생성 순서와 반대입니다.

 

관리 레코드를 파라미터로 전달

관리 레코드는 파라미터로 전달되거나 함수에 의해 반환될 때 일반 레코드와 다르게 동작할 수 있습니다. 다음은 다양한 시나리오를 보여주는 몇가지 루틴입니다.

procedure ParByValue (rec: TMyRecord);
procedure ParByConstValue (const rec: TMyRecord);
procedure ParByRef (var rec: TMyRecord);
procedure ParByConstRef (const [ref] rec: TMyRecord);
function ParReturned: TMyRecord;

각 로그를 하나씩 검토하지 않고도 다음과 같이 정보를 요약할 수 있습니다.

  • ParByValue - 새로운 레코드를 생성하고, 할당 연산자가 있는 경우 데이터를 복사 후 프로시저를 종료할때 임시 복사본 삭제
  • ParByConstValue - 데이터를 복사하지 않고, 할당 연산자를 호출하지 않음
  • ParByRef - 데이터를 복사하지 않고, 할당 연산자를 호출하지 않음
  • ParByConstRef -  데이터를 복사하지 않고, 할당 연산자를 호출하지 않음
  • ParReturned - (초기화를 통해)새 레코드를 만들고 반환 시 Assign 연산자를 호출. “my1 := ParReturnd;”과 같이 호출된다면, 반환된 레코드를 다시 할당 후 다시 레코드 삭제됨

 

예외(Exception)와 관리 레코드

예외 발생 시 객체와 달리, 명시적인 try finally 블록이 없어도 레코드가 제거됩니다. 이는 근본적인 객체와 차이점이자 관리 레코드의 실질적으로 유용한점입니다.

procedure ExceptionTest;
begin
  var a: TMRE;
  var b: TMRE;
  raise Exception.Create('Error Message');
end;

이 프로시저 내에서, 두개의 생성자 호출과 두개의 소멸자 호출이 있습니다. 다시 말하자면 이것은 관리 레코드의 근본적인 차이점이자 주요 특징입니다. 관리 레코드를 기반으로 하는 간단한 스마트 포인터는 다음 섹션을 참조하세요.

 

관리 레코드 배열

관리 레코드 정적 배열을 선언하면, 배열 선언시 초기화 연산자를 호출합니다.

var
  a1: array [1..5] of TMyRecord; // call here
begin
  Log ('ArrOfRec');

배열은 범위를 벗어나면 모두 파괴됩니다. 관리 레코드의 동적 배열을 선언하는 경우, 배열 크기(SetLength)를 사용 시 초기화 코드를 호출합니다.

var
  a2: array of TMyRecord;
begin
  Log ('ArrOfDyn');  
  SetLength(a2, 5); // call here

결론

델파이 언어에 추가된 훌륭한 새로운 기능 중 일부를 간략하게 소개했습니다. 제너릭 기반 관리 레코드 등을 비롯해 여기서 소개하지 못한 다양한 시나리오에서도 활용할 수 있습니다. 여기에 소개된 새로운 기능은 언어에 관한 것이지만, 모든 플랫폼에 적용되는 통합 메모리 관리 등 정말 멋진 새로운 기능있습니다. 기대하세요!

10.4 무료 평가판으로 지금 경험해보세요!   [전체보기] 10.4 새기능들!