Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Ошибка после удаления интерфейса [Delphi, Windows]


Семен Сорокин ©   (2005-05-18 17:36:00) 

Приветствую, пол дня бьюсь над одной проблемой:

имеется следующее описание:

unit Unit1;

interface

uses
 Classes;

type
 TNoRefrencedObject = class(TObject)
 protected
   function QueryInterface(const IID: TGUID; out Obj): HResult; virtual; stdcall;
   function _AddRef: Integer; stdcall;
   function _Release: Integer; stdcall;
 end;

 TBaseListClass = class(TNoRefrencedObject, IUnknown)
 private
   FItems: TStrings;
 public
   constructor Create;
   destructor Destroy; override;
 end;

 IInterface1 = interface
 ['{7C9DD5BE-DCE3-4A5B-889C-EF3CD7A754DC}']
   function GetData: integer;
 end;

 TClass1 = class(TNoRefrencedObject, IInterface1)
 private
   FData: integer;
 public
   function GetData: integer;
 end;

 IInterface2 = interface
 ['{B1E31834-213D-4519-AC3B-5A5C278006B6}']
   function GetInterface1(AIndex: integer): IInterface1;
 end;

 TClass2 = class(TBaseListClass, IInterface2)
 public
   function GetInterface1(AIndex: integer): IInterface1;
   destructor Destroy; override;
 end;

 IInterface3 = interface
 ['{3FB223AB-8EA5-46CB-B3F1-AB3CF5698F8B}']
   function GetInterface2: IInterface2;
 end;

 TClass3 = class(TInterfacedObject, IInterface3)
 private
   FClass2: TClass2;
 public
   function GetInterface2: IInterface2;
   constructor Create;
   destructor Destroy; override;
 end;

implementation

{ TNoRefrencedObject }

function TNoRefrencedObject._AddRef: Integer;
begin
Result := -1
end;

function TNoRefrencedObject._Release: Integer;
begin
Result := -1
end;

function TNoRefrencedObject.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
if GetInterface(IID, Obj) then
 Result := S_OK
else
 Result := E_NOINTERFACE
end;

{ TBaseListClass }

constructor TBaseListClass.Create;
begin
FItems := TStringList.Create;
end;

destructor TBaseListClass.Destroy;
begin
FItems.Free;
inherited;
end;

{ TClass1 }

function TClass1.GetData: integer;
begin
Result := FData
end;

{ TClass2 }

destructor TClass2.Destroy;
begin
 TClass1(FItems.Objects[0]).Free;
 inherited;
end;

function TClass2.GetInterface1(AIndex: integer): IInterface1;
begin
Result := TClass1(FItems.Objects[AIndex])
end;

{ TClass3 }

constructor TClass3.Create;
var
 cl1: TClass1;
begin
FClass2 := TClass2.Create;
cl1 := TClass1.Create;
cl1.FData := 555;
FClass2.FItems.AddObject('xxx', cl1)
end;

destructor TClass3.Destroy;
begin
FClass2.Free;
inherited Destroy;
end;

function TClass3.GetInterface2: IInterface2;
begin
Result := FClass2
end;

end.


и сам проект:

program Project1;
uses
 SysUtils,
 Unit1 in 'Unit1.pas';

var
 _main: IInterface3;
 _int1: IInterface1;
 _data: integer;
begin
_main := TClass3.Create;
// если закоментарить следующую строку, то ошибки не будет
_int1 := _main.GetInterface2.GetInterface1(0);  
_int1 := nil;
_main := nil
end.

После выполнения кода (после убийства _main) происходит AV.
Подскажите где я не прав.


Семен Сорокин   (2005-05-20 11:50:00) [1]


> Slym ©   (20.05.05 06:17) [10]
>
> >_main := TClass3.Create;
> > _main := nil;
>
> Ты думаешь сдесь нет утечки памяти?... Есть!
> Где ты ты вызываешь деструктор? Нигде!
> Интерфейсы с подсчетом, сами себе деструктор вызывают когда
> RefCount=0


Думаю здесь нет :)) даже знаю, потому как
TClass3 = class( TInterfacedObject, IInterface3)

вопрос снят


Slym   (2005-05-20 06:17:00) [2]


>_main := TClass3.Create;
> _main := nil;

Ты думаешь сдесь нет утечки памяти?... Есть!
Где ты ты вызываешь деструктор? Нигде!
Интерфейсы с подсчетом, сами себе деструктор вызывают когда RefCount=0

Obj (Экземпляр TClass)- Это указатель на объект, т.е. при передачи объекта (как результата функции, или входного параметра) используется указатель на объект.
(@Obj/Obj^)

Interface - Это указатель на указатель объекта, т.е. при передаче (как результата функции, или входного параметра) используется указатель на указаетель объекта.(@@Obj/Obj^^)

Посмотри Демосы QRegister


Семен Сорокин   (2005-05-19 18:17:00) [3]


> Shaman_Naydak;   (19.05.05 17:51) [8]

Благодарю, все понятно.


Shaman_Naydak;   (2005-05-19 17:51:00) [4]

Молодой человек, послушайте старого и мудрого человека...
Все дело в скрытых и неявных переменных!
Когда вы вызываете функцию, возвращающую интерфейс, то у вас
заводится стековая переменная, отвечающая за эту переменную.
А уж затем производится копирование в глобальную переменную, которую вы задали..
Вот если бы вы перенесли весь код в процедуру, а не в главной программе.. то у вас переменные стали бы локальными и у дельфи хватит ума не создавать по второй копии переменной для хранения интерфейса.. Ферштейн?
Другой способ решения вопроса - использование процедур с out параметром... тут тоже не будет стековых переменных...

И наконец.. не читайте до обеда советских газет..
то есть не используйте неразумно неподсчитываемые интерфейсы...!
За сим разрешите откланяться ;))


Семен Сорокин   (2005-05-19 11:33:00) [5]


> Slym ©   (19.05.05 11:03) [6]
> TClass - Это указатель на объект
> IInterface - Это указатель на указатель объекта!!!

Что Вы хотите этим сказать?
TClass - это указатель на тип объекта, а IInterface - указатель на тип интерфейса


Slym   (2005-05-19 11:03:00) [6]

TClass - Это указатель на объект
IInterface - Это указатель на указатель объекта!!!


Семен Сорокин   (2005-05-19 09:57:00) [7]


> Shaman_Naydak;   (18.05.05 19:35) [4]
> Создается неявная переменная из-за _main.GetInterface2
> И освобождаться она будет.. когда?.. прально.. уже на выходе
> из метода, когда объект тю-тю..


а здесь Вы не правы, потому как _main.GetInterface2 - возвращает неподсчитываемый интерфейс, вот код - ошибка та же:

var
 _main: IInterface3;
 _int1: IInterface1;
 _int2: IInterface2;
 _data: integer;
begin
 _main := TClass3.Create;
 _int2 := _main.GetInterface2;
 _int1 := _int2.GetInterface1(0);
 _data := _int1.GetData;
 _int1 := nil;
 _int2 := nil;
 _main := nil
end.



> отсюда мораль.. смешивать подсчитываемые ссылки и неподситываемые
> не самая лучшая затея

в даннолм случе с подсчитываемыми ссылками у сменяя только главный (IInterface3) интерфейс.

ЗЫ. Проблема осталась :(


Shaman_Naydak;   (2005-05-18 19:35:00) [8]

Создается неявная переменная из-за _main.GetInterface2
И освобождаться она будет.. когда?.. прально.. уже на выходе из метода, когда объект тю-тю..
отсюда мораль.. смешивать подсчитываемые ссылки и неподситываемые не самая лучшая затея


Семен Сорокин   (2005-05-18 17:53:00) [9]

если закоментарить строку
TClass1(FItems.Objects[0]).Free;
в деструкторе TClass2 то AV не возникает, но, имхо, тогда будет утечка памяти.

После _int1 := nil, объект не освобождается т.к. он унаследован от TNoRefrencedObject.


Иван Шихалев   (2005-05-18 17:49:00) [10]

Хотя... сорри, гоню


Иван Шихалев   (2005-05-18 17:47:00) [11]

После _int1 := nil, объект освобождается, поскольку _Release = -1


Наверх

Титульная страница Поиск, карта сайта Написать письмо