Melewati referensi objek sebagai Antarmuka

Saya meneruskan objek yang dibuat ke konstruktor objek lain yang memerlukan Antarmuka yang diimplementasikan oleh objek tersebut.

  ISomeInterface = interface
  ['{840D46BA-B9FB-4273-BF56-AD0BE40AA3F9}']
  end;

  TSomeObject = class(TInterfacedObject, ISomeinterface)
  end;

  TSomeObject2 = class
  private
    FSomeInterface: ISomeinterface;
  public
    constructor Create(SomeObject: ISomeInterface);
  end;

var
Form1: TForm1; // main form
SomeObject: TSomeObject;

constructor TSomeObject2.Create(SomeObject: ISomeInterface);
begin
  FSomeInterface := SomeObject;
end;

// main form creating
procedure TForm1.FormCreate(Sender: TObject);
var SomeObject2: TSomeObject2;
begin
  SomeObject := TSomeObject.Create;
  //  SomeObject2 := TSomeObject2.Create(nil);        // ok
  SomeObject2 := TSomeObject2.Create(SomeObject);     // not ok
  try
  // do some things
  finally
    SomeObject2.Free;
  end;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  SomeObject.Free; // if passed to a SomeObject2 Constructor - freeing it causing av
end;

Setelah saya menutup formulir utama, saya mendapat AV dan kebocoran memori - seluruh formulir utama bocor. Jika saya meneruskan nil ke konstruktor TSomeObject semuanya baik-baik saja. Apakah kompilator membebaskan FSomeInterface dengan penghitungan referensi dan saya tidak boleh mencoba membebaskan SomeObject di mainForm destructor? Bagaimana saya bisa menghindarinya?


person JustMe    schedule 29.04.2013    source sumber
comment
Inilah yang terjadi jika Anda mencampur referensi objek dan antarmuka...dapat menyebabkan bug yang sangat buruk.   -  person jpfollenius    schedule 29.04.2013


Jawaban (1)


TSomeObject diwarisi dari TInterfacedObject dan dengan demikian referensi dihitung. Contoh TSomeObject Anda tidak dihitung sebagai referensi dan harus dihapus atau diganti dengan variabel antarmuka.

Jika Anda memerlukan instance TSomeObject yang dibuat di FormCreate, Anda harus menetapkannya ke variabel bertipe ISomeInterface, sehingga penghitungan referensi juga dapat digunakan.

Pendekatan lain adalah dengan mewarisi dari TInterfacedPersistant daripada TInterfacedObject untuk menghindari penghitungan referensi.

Untuk menjelaskan apa yang terjadi dalam kode Anda:

procedure TForm1.FormCreate(Sender: TObject);
var SomeObject2: TSomeObject2;
begin
  { Here you create the instance and assign it to a variable holding the instance.
    After this line the reference count of the instance is 0 }
  SomeObject := TSomeObject.Create;
  //  SomeObject2 := TSomeObject2.Create(nil);        // ok
  { Using the instance as a parameter will increase the reference count to 1 }
  SomeObject2 := TSomeObject2.Create(SomeObject);     // not ok
  try
  // do some things
  finally
    { Freeing SomeObject2 also destroys the interface reference FSomeInterface is
      pointing to (which is SomeObject), decreasing the reference count to 0, which
      in turn frees the instance of TSomeObject. }
    SomeObject2.Free;
  end;
  { Now, after SomeObject is freed, the variable points to invalid memory causing the
    AV in FormDestroy. }
end;
person Uwe Raabe    schedule 29.04.2013