การส่งผ่านการอ้างอิงวัตถุเป็นอินเทอร์เฟซ

ฉันกำลังส่งวัตถุที่สร้างขึ้นไปยังตัวสร้างของวัตถุอื่นซึ่งต้องการอินเทอร์เฟซที่วัตถุนั้นนำไปใช้

  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;

หลังจากที่ฉันปิดฟอร์มหลัก มันทำให้ฉันมี AV และหน่วยความจำรั่ว - ฟอร์มหลักทั้งหมดรั่วไหล ถ้าฉันส่ง nil ไปยังตัวสร้าง TSomeObject ทุกอย่างก็ดี ตัวคอมไพเลอร์ทำให้ว่าง FSomeInterface โดยการนับอ้างอิงและฉันไม่ควรพยายามปล่อย SomeObject ใน mainForm destructor หรือไม่ ฉันจะหลีกเลี่ยงมันได้อย่างไร?


person JustMe    schedule 29.04.2013    source แหล่งที่มา
comment
นี่คือสิ่งที่เกิดขึ้นหากคุณผสมการอ้างอิงอ็อบเจ็กต์และอินเทอร์เฟสเข้าด้วยกัน...อาจทำให้เกิดข้อผิดพลาดที่น่ารังเกียจได้   -  person jpfollenius    schedule 29.04.2013


คำตอบ (1)


TSomeObject สืบทอดมาจาก TInterfacedObject และดังนั้นจึงนับการอ้างอิง อินสแตนซ์ TSomeObject ของคุณไม่นับการอ้างอิง และควรถูกลบหรือแทนที่ด้วยตัวแปรอินเทอร์เฟซ

หากคุณต้องการอินสแตนซ์ของ TSomeObject ที่สร้างใน FormCreate คุณควรกำหนดอินสแตนซ์ดังกล่าวให้กับตัวแปรประเภท ISomeInterface เพื่อให้การนับการอ้างอิงทำงานได้เช่นกัน

อีกวิธีหนึ่งคือการสืบทอดจาก TInterfacedPersistant แทน TInterfacedObject เพื่อหลีกเลี่ยงการนับการอ้างอิง

เพื่ออธิบายสิ่งที่เกิดขึ้นในโค้ดของคุณ:

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