이 글은 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 새기능들!

 

번호 제목 글쓴이 날짜 조회 수
공지 [DelphiCon 요약] 코드사이트 로깅 실전 활용 기법 (Real-world CodeSite Logging Techniques) 관리자 2021.01.19 12180
공지 [UX Summit 요약] 오른쪽 클릭은 옳다 (Right Click is Right) 관리자 2020.11.16 11777
공지 [10.4 시드니] What's NEW! 신기능 자세히 보기 관리자 2020.05.27 14193
공지 RAD스튜디오(델파이,C++빌더) - 고객 사례 목록 관리자 2018.10.23 19395
공지 [데브기어 컨설팅] 모바일 앱 & 업그레이드 마이그레이션 [1] 관리자 2017.02.06 20997
공지 [전체 목록] 이 달의 기술자료 & 기술레터 관리자 2017.02.06 16772
공지 RAD스튜디오(델파이, C++빌더) - 시작하기 [1] 관리자 2015.06.30 36448
공지 RAD스튜디오(델파이,C++빌더) - 모바일 앱 개발 사례 (2020년 11월 업데이트 됨) 험프리 2014.01.16 171791
1220 [10.4 시드니 신기능] 겟잇 패키지 매니저(GetIt Package Manager) 개선 험프리 2020.05.21 528
1219 [10.4 시드니 신기능] 컨트롤 개별 VCL 스타일 적용(Per-Control Style) 적용 험프리 2020.05.19 832
1218 [10.4 시드니 신기능] 새로운 VCL TEdgeBrowser 컴포넌트 험프리 2020.05.18 18953
» [10.4] 커스텀 매니지드 레코드(Custom Managed Records) 험프리 2020.05.14 982
1216 [고객 사례- 솔루션, 델파이] Beyond Compare - 데이터, 시스템 비교/병합/관리 프로그램 관리자 2020.05.14 649
1215 델파이, 25년의 혁신 - 버전 1부터 10.3까지 버전별 핵심 기능 [2] 관리자 2020.05.12 1697
1214 [고객 사례- 솔루션, 델파이] AlignMix - 시각화된 세일즈 관리 도구 관리자 2020.05.12 320
1213 [10.4 시드니 신기능] 다시 태어난 '코드 인사이트' [2] file 험프리 2020.05.08 1406
1212 [발표자료] 20200429 델파이 Push 메시지 전송 시스템 구현 방법 with 구글 Firebase [3] file 관리자 2020.05.04 793
1211 헬스케어 분야에서의 델파이 - 코로나에 맞서며 file 김원경 2020.04.29 537
1210 [고객 사례- 의료, 델파이] COVID-19 격리 대상자 상태 관리 앱 관리자 2020.04.28 595
1209 이 달의 기술자료 - 2020년 05월 file 험프리 2020.04.24 328
1208 [사례 소개] 데브기어 마이그레이션 유상 컨설팅 사례 험프리 2020.04.22 437
1207 인터베이스와 FireDac에서 배열필드 사용하기 file 김원경 2020.04.16 357
1206 인터베이스, FireDAC 및 TEMSDataSetResource를 사용하여 RAD 서버에서 자동 증가 필드를 생성하는 방법 file 김원경 2020.04.16 358
1205 델파이용 벡터 컨테이너 file 김원경 2020.04.14 606
1204 [프로그래밍 애피타이저] 개발이 처음이거나 비 전공자 분들을 위한 가장 첫 번째 STEP! 관리자 2020.04.13 2081
1203 인터베이스(InterBase) 2020 버전을 사용해야 하는 5 가지 이유 file 김원경 2020.04.09 5919
1202 [프로그래밍 애피타이저] 10장 트랜잭션의 정의 file 김원경 2020.04.09 388
1201 [프로그래밍 애피타이저] 9장 저장프로시저와 트리거 file 김원경 2020.04.09 462