2012-10-12 2 views
9

여기 까다로운 상황이 있습니다. 나는 레코드의 필드 인 객체를 해제 할 수 있어야합니다. 일반적으로 소멸자에 클린업 코드를 작성합니다 (클래스 인 경우). 그러나 레코드 유형은 "소멸자"를 도입 할 수 없으므로 어떻게 호출 할 수 있습니까? TObject (Field) .Free;? 를 새 기록을 교체레코드에있는 오브젝트를 해제하는 방법은 무엇입니까?

  1. :

    두 사용의 유형 I 예측이있을 수 있습니다.

    이 사용법은 쉽게 구현할 수 있다고 생각합니다. 레코드는 값 유형이므로 할당시 복사되므로 할당 연산자가 오버로드되어 이전 레코드가 소유 한 객체를 해제 할 수 있습니다.

    (편집 : 할당 오버로드는 수 없습니다 그것은 나에게 새로운 정보입니다 ....)

  2. 레코드 변수가 정의 된 범위를 종료.

    개체를 비우는 개인 메서드가 있다고 생각할 수 있습니다.이 메서드는 범위 여기를 수동으로 호출 할 수 있습니다. 하지만 여기에 같은 질문이 있습니다 : 더 많이 만드는 방법 녹음?

    FAdditionalData:=TList<Pointer>.Crete; 
    

    라고

    TProperties = record 
        ... some other spesific typed fields: Integers, pointers etc.. 
        FBaseData: Pointer; 
    
        FAdditionalData: TList<Pointer>; 
        //FAdditionalData: array of Pointer; this was the first intended definition 
    end; 
    

    가 가정 :이 동작은 가지 여기

이 (분명하지 의도 된 사용 등) 샘플입니다 ... 클래스 같은 느낌 레코드 생성자에서 또는 수동으로 필드에 공개적으로 액세스하여 레코드 변수 범위에서 수동으로

,210

procedure TFormX.ButtonXClick(Sender: TObject); 
var 
    rec: TProperties; 
begin 
    //rec:=TProperties.Create(with some parameters); 

    rec.FAdditionalData:=TList<Pointer>.Create; 

    //do some work with rec 
end; 
레코드에있는 모든 개체 참조 인 경우 ButtonClick 상기 녹화 더 이상 범위하지만 TList를 여전히 메모리 누수 원인의 실존을 유지 ...

+1

레코드 할당을 오버로드 할 수 없습니다. – kludg

+0

나는 (이전에는 절대로 필요하지 않은) 그것을 깨닫지 못했다. 그러나 나는 지금 그것을 배웠다. :) 그래, 그것은 과부하가 될 수 없었다. ... –

답변

10

을 종료 한 후, 그렇다면 컴파일러를 사용하여 도움을받을 수 없습니다. 당신은 그 대상의 일생을 전적으로 책임지고 있습니다. 할당 연산자를 오버로드 할 수 없으며 범위 완료에 대한 알림을받지 못합니다.

당신이 할 수있는 일은 객체의 수명을 관리하는 가드 인터페이스를 추가하는 것입니다.

TMyRecord = record 
    obj: TMyObject; 
    guard: IInterface; 
end; 

TMyObject은 참조 횟수를 기준으로 수명을 관리해야합니다. 예를 들어 TInterfacedObject에서 파생됩니다. 당신이 기록을 초기화 할 때

이 수행이 시점에서

rec.obj := TMyObject.Create; 
rec.guard := rec.obj; 

를, 레코드의 guard 필드는 이제 개체의 수명을 관리합니다.

사실,이 아이디어를 더 깊이 실천하고 싶다면 전용 클래스를 만들어 개체의 수명을 보호 할 수 있습니다. 그러면 더 이상 클래스에 IInterface을 구현하지 않아도됩니다. 웹에는 기술을 설명하는 예제가 많이 있습니다. 예를 들어, Jarrod Hollingworth의 기사 인 Smart Pointers과 Barry Kelly의 Reference-counted pointers, revisited이라는 제목의 기사를 제공합니다. 거기에 더 많은 것들이 있습니다. 그것은 오래된 트릭입니다!

그러나 여기에있는 것은 값 유형과 참조 유형의 이상한 혼합입니다. 그것의면에서, 레코드는 값 유형입니다. 그러나 이것은 참조 유형과 같은 역할을합니다. 값 유형 인 레코드의 다른 필드가있는 경우 이는 훨씬 혼란 스럽습니다. 이러한 기록을 가지고 작업 할 때이 문제를 잘 알고 있어야합니다.

디자인에 대해 더 많이 알지 못해도 레코드에 객체 참조를 두지 말 것을 권합니다. 참조 유형, 즉 클래스 내부에 더 잘 맞습니다.

+7

나는 마지막 단락으로 앞서가는 것이 좋습니다. 클래스를 레코드에 넣는 것은 평생 관리에 매우 조심스럽지 않으면 코드를 버깅하는 빠른 경로입니다. – afrazier

+0

매우 유익한 답변, Heffernan 씨에게 감사드립니다. 나는 그 길을 실험 할 것입니다. 설계를보다 명확하게하기 위해 질문을 업데이트해야합니다. 객체 필드는 ** TList ** s입니다. 그러나 처음부터 그랬던 것처럼 되돌릴 것 같습니다 : ** 포인터의 배열 ** –

+1

동적 배열을 사용하면 수명 관리를 해결할 수 있습니다. Synopse의 우수한 TDynArray를 사용하여 동적 배열을보다 쉽게 ​​사용할 수 있습니다. –

3

누군가 TLifetimeWatcher라는 클래스를 만들었습니다.

TLifetimeWatcher = class(TInterfacedObject) 
private 
    fInstance: TObject; 
    fProc: TProc; 
public 
    constructor Create(instance: TObject); overload; 
    constructor Create(instance: TObject; proc: TProc); overload; 
    destructor Destroy; override; 
end; 

// (정리) PROC가 할당되면 소멸자에서 실행됩니다, 그렇지 않으면 인스턴스가 무료 메소드를 호출하여 해제됩니다 기본적으로, 그것은 것 같습니다.

+0

이것은 내 대답에 설명 된 기본 접근법이다. 구현이 없으면 많은 용도가 아닙니다. –

+2

'누군가'는 Barry Kelly입니다. http://blog.barrkel.com/2008/09/smart-pointers-in-delphi.html – kludg

관련 문제