오래된 Delphi 코드에서 제가 가장 자주 만나는 수명 실수는 한 인스턴스에 두 가지 소유 규칙을 섞는 데서 나옵니다. TInterfacedObject를 상속한 클래스는 인터페이스 참조 카운트가 0이 될 때 해제됩니다. 클래스 타입의 변수는 전혀 세지 않습니다. 같은 인스턴스를 두 종류의 참조로 함께 붙잡으면, 두 규칙은 그 객체가 언제 사라지는지에 대해 서로 다른 답을 냅니다.
program Greeter;
{$APPTYPE CONSOLE}
type
IGreeter = interface
procedure Hello;
end;
TGreeter = class(TInterfacedObject, IGreeter)
procedure Hello;
end;
procedure TGreeter.Hello;
begin
WriteLn('hello');
end;
procedure Use(G: IGreeter);
begin
G.Hello;
end;
var
Obj: TGreeter;
begin
Obj := TGreeter.Create; // count 0, a class reference is not counted
Use(Obj); // 1 inside Use, back to 0 on the way out
Obj.Free; // the object is already gone
end.
인스턴스는 카운트 0으로 시작합니다. 클래스 참조는 카운트를 건드리지 않기 때문입니다. Use에 넘기면 IGreeter로 변환되고, 호출하는 동안 카운트가 1이 되었다가 빠져나오면서 0으로 돌아가며, 0이 되는 순간 객체는 스스로를 파괴합니다. 그 뒤의 Obj.Free는 이미 해제된 메모리를 두 번째로 파괴하는 것입니다. 컴파일러는 이 모두를 받아들입니다.
제 규칙은 이렇습니다. 클래스가 TInterfacedObject로 인터페이스를 구현하는 순간부터, 인스턴스를 오래 붙잡는 참조는 모두 인터페이스 참조로 두고, 클래스 타입은 생성자를 부르는 줄에만 나타나게 합니다. 다른 분들도 같은 자리에 선을 긋는지 궁금합니다.
