Rambler's Top100
"Knowledge itself is power"
F.Bacon
Поиск | Карта сайта | Помощь | О проекте | ТТХ  
 Подводные камни
  
О разделе

Список по категориям
Общий список

 
 К н и г и
 
Книжная полка
 
 
Библиотека
 
  
  
 


Поиск
 
Поиск по КС
Поиск в статьях
Яndex© + Google©
Поиск книг

 
  
Тематический каталог
Все манускрипты

 
  
Карта VCL
ОШИБКИ
Сообщения системы

 
Форумы
 
Круглый стол
Новые вопросы

 
  
Базарная площадь
Городская площадь

 
   
С Л С

 
Летопись
 
Королевские Хроники
Рыцарский Зал
Глас народа!

 
  
ТТХ
Конкурсы
Королевская клюква

 
Разделы
 
Hello, World!
Лицей

Квинтана

 
  
Сокровищница
Подземелье Магов
Подводные камни
Свитки

 
  
Школа ОБЕРОНА

 
  
Арсенальная башня
Фолианты
Полигон

 
  
Книга Песка
Дальние земли

 
  
АРХИВЫ

 
 

Сейчас на сайте присутствуют:
 
  
 
Во Флориде и в Королевстве сейчас  14:21[Войти] | [Зарегистрироваться]

Интерфейсы.Access Violation в _IntfClear для TComponent

Алексей Михайличенко
дата публикации 13-04-2007 02:16

КАТЕГОРИЯКОМПИЛЯТОР.Интерфейсы.Access Violation в _IntfClear для TComponent
ПРОДУКТDelphi 7
ПЛАТФОРМАWindows


Речь идет об использовании интерфейсных ссылок на объекты — потомки TComponent. В отличие от потомков TInterfacedObject, уничтожение которых происходит автоматически по подсчету ссылок, потомки TComponent уничтожаются другими методами, обычно через владельца (Owner) или вручную, а использование интерфейсных ссылок на них применяется как аналог множественного наследования.

Однако известно, что для интерфейсных ссылок на потомков TComponent все равно происходит вызов _Release и _IntfClear при присваивании nil или выходе локальных переменных из зоны видимости. При этом, если интерфейсная ссылка является "мусорной" (указывает на уже разрушенный объект), то происходит Access Violation. Общепринятой практикой является присваивание nil всем интерфейсным ссылкам на объект до его разрушения,чтобы не допустить появления "мусорных" ссылок, примерно по такому алгоритму:

  1. Инициализация объектов-реализаторов и интерфейсных ссылок
  2. Использование интерфейсов
  3. Присвоение nil всем интерфейсным ссылкам
  4. Уничтожение объектов

Оказывается, что даже соблюдение этого правила не всегда спасает от Access Violation. Дело в том, что если на шаге 2 (Использование интерфейсов) используются конструкция, подобная следующей:

procedure Test
begin
//1.Инициализация объектов-реализаторов и интерфейсных ссылок
...
// 2. Использование интерфейсов
    FuncGetInterface.DoInterfaceMethod(...);
// 3. Присвоение nil всем интерфейсным ссылкам. - а нам и присвоить нечему
//4. Уничтожение объектов
...
end; <<-- здесь Access Violation

то есть, если есть функция, возвращающая интерфейс для вызова какого-либо метода интерфейса, то при завершении процедуры Test мы получим Access Violation в System._IntfClear. Дело в том, что при вызове функции, возвращающей интерфейс, создается "невидимая" переменная - интерфейсная ссылка, для которой в конце процедуры Test, в связи с выходом ее из зоны видимости, вызывается _IntfClear. А объект к этому моменту уже разрушен.

Пример для воспроизведения проблемы:
(на форме Form1 две кнопки - Button2 и Button3)

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  ITest = interface // есть некий интерфейс
    procedure Hello;
  end;

  TTest = class(TComponent, ITest) // есть класс, его реализующий
    procedure Hello;
  end;

  TForm1 = class(TForm) // класс-контейнер
    Button2: TButton;
    Button3: TButton;
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    objTest: TTest;
    intfTest: ITest;
    procedure InitInterface; // это создается объект-реализатор
    procedure FreeInterface; // это он чистится
    function GetInterface: ITest; // вот самое главное - функция,
возвращающая интерфейс
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TTest }

procedure TTest.Hello;
begin
  ShowMessage('Hello');
end;

{ TForm1 }

procedure TForm1.InitInterface; // здесь все понятно
begin
  objTest := TTest.Create(nil);
  intfTest := objTest;
end;

procedure TForm1.FreeInterface; // здесь тоже все верно
begin
  intfTest := nil; // сначала обнуляем интерфейсную ссылку, чтобы не
                     //вызывались _Release на мусоре
  objTest.Free;    // затем чистим объект
end;

function TForm1.GetInterface: ITest; // собственно злополучная функция
begin
  Result := self.intfTest;
end;

//*********************************************************//

procedure TForm1.Button2Click(Sender: TObject); // самое интересное
begin
  // вроде бы здравый порядок работы с ресурсами: 
  // создаем, используем,осовобождаем
  self.InitInterface;
  self.GetInterface.Hello;  // строка X
  self.FreeInterface;

  // какие-либо еще действия с памятью
  with TButton.Create(self) do begin
    Parent := self;
  end;

  // а по выходу будет Access Violation, из-за вызова 
  // _IntfClear для "внутренней локальной переменной",
  // которая, оказывается, создается в строке X и 
  // содержит ссылку на интерфейс
end;

procedure TForm1.Button3Click(Sender: TObject);
var tempIntf: ITest;
begin
  self.InitInterface;

  // все отличие — здесь мы создаем эту переменную в явном виде,
  // и затем явно делаем ей nil, пока объект еще жив, и все замечательно
  tempIntf := self.GetInterface;
  tempIntf.Hello;
  tempIntf := nil; // а если убрать это, то будет AV на выходе, как в Button2Click

  self.FreeInterface;

  // какие-либо действия с памятью
  with TButton.Create(self) do begin
    Parent := self;
  end;

  // здесь все будет нормально
end;

end.




Типовые решения
Для обхода проблемы можно создавать временную переменную — интерфейсную ссылку, и присваивать ей nil явно, до уничтожения объекта.

var TempIntf: MyInterface;

TempIntf := FuncGetInterface;
TempIntf.DoInterfaceMethod(...);
TempIntf := nil;




Смотрите также материалы по темам:
[TComponent] [Жизненный цикл] [Интерфейсы]

 Обсуждение материала [ 15-06-2008 15:08 ] 50 сообщений
  
Время на сайте: GMT минус 5 часов

Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter.
Функция может не работать в некоторых версиях броузеров.

Web hosting for this web site provided by DotNetPark (ASP.NET, SharePoint, MS SQL hosting)  
Software for IIS, Hyper-V, MS SQL. Tools for Windows server administrators. Server migration utilities  

 
© При использовании любых материалов «Королевства Delphi» необходимо указывать источник информации. Перепечатка авторских статей возможна только при согласии всех авторов и администрации сайта.
Все используемые на сайте торговые марки являются собственностью их производителей.

Яндекс цитирования