| | | | |
КОМПИЛЯТОР.Не производится финализация интерфейсных ссылок в основном модуле | Полный текст материала
Другие публикации автора: Алексей Вуколов
Цитата или краткий комментарий: «... Согласно концепции интерфейсных ссылок, они должны обнуляться при выходе из области видимости. И это правило работает нормально, если такие ссылки объявлены как поля в классах или как переменные в процедурах и модулях.
...» |
Важно:- Страница предназначена для обсуждения материала, его содержания, полезности, соответствия действительности и так далее. Смысл не в разборке, а в приближении к истине :о) и пользе для всех.
- Любые другие сообщения или вопросы, а так же личные эмоции в адрес авторов и полемика, не относящаяся к теме обсуждаемого материала, будут удаляться без предупреждения авторов, дабы не мешать жителям нормально общаться.
- Размер одного сообщений не должен превышать 5К. Если Вам нужно сказать больше, сделайте это за два раза. Или, что в данной ситуации правильнее, напишите свою статью.
Всегда легче осудить сделанное, нежели сделать самому. Поэтому, пожалуйста, соблюдайте правила Королевства и уважайте друг друга.
Добавить свое мнение.
[Интерфейсы COM-объектов] [Классы] [Жизненный цикл]
Отслеживать это обсуждение
Всего сообщений: 2320-11-2006 17:11Вообще-то, всегда думал, что это нормальное поведение компилятора и никогда не закладывался на автоматическую финализацию глобальных переменных.
Нет не нормальное, не надо забывать о том что есть такая штука как пакеты (ну и DLL тоже) и их загрузка/выгрузка не должна приводить к ошибкам и утечкам.
К тому же компилятор всё это учитывает, посмотрите на код который он вставляет в finalize (ставьте метку на "end."), там финализируется всё, и интерфейсы, и строки, и массивы, и локальные типизированные константы, а то что описано в статье это просто глупый баг, забыли что-то где-то добавить для интерфейсов и всё. |
|
20-11-2006 03:14Вообще-то, всегда думал, что это нормальное поведение компилятора и никогда не закладывался на автоматическую финализацию глобальных переменных. |
|
03-11-2006 10:15сообщение от автора материала >>>это не баг а нормальная ситуация, переменная не вышла за область своей видимости,undefined
Время жизни такой переменной равно ремени жизни пограммы и переменная выходит за область видимости, когда программа завершается. Так что все-аки баг.
Что же касается строк и других ресурсов, то строки находятся в куче и умрут вместе с ней, поэтому их можно и не финализировать. А вот с интерфейсами сиуация особая. |
|
02-11-2006 02:14ДА чего вы спорите это не баг а нормальная ситуация, переменная не вышла за область своей видимости, ведь вы ее инициализируете в коде инициализации, а кода финализации нет, вот компилятору и некуда вставлять свой код. Касается, что главного модуля проекта, что секции initialization остальных модулей :) |
|
12-10-2006 00:50хочу дополнить что это относиться не тока к интерфейсам, но и к строкам и другим ресурсам, с автоматическим уничтожениям... |
|
02-02-2004 14:21>ОДНАКО, если в примере к "камню" вызвать в теле
>программы метод i._release, то деструктор
>вызываеться.
Понятное дело, вызывается. Но дело в том, что по всем правилам вызов _Release не нужен. Мало того, это может повлиять на правильность подсчета ссылок. |
|
02-02-2004 06:07ОДНАКО, если в примере к "камню" вызвать в теле программы метод i._release, то деструктор вызываеться. |
|
20-03-2003 11:44сообщение от автора материала >Так что привычку не заводить глобальные переменные без инициализации
>надо распространить не только на модули, но и на весь проект.
Так именно про это я и писал! :o)
|
|
20-03-2003 11:21to Алексей Вуколов
Когда я запустил такой код:
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils,
Unit1 in "Unit1.pas";
type
TSomeObject = class(TInterfacedObject)
constructor Create;
destructor Destroy; override;
end;
{ TSomeObject }
constructor TSomeObject.Create;
begin
writeln("Created");
end;
destructor TSomeObject.Destroy;
begin
writeln("Destroyed");
inherited;
end;
var
i : IUnknown = nil;
begin
i := TSomeObject.Create;
end.
то объект i уничтожился. Так что привычку не заводить глобальные переменные без инициализации надо распространить не только на модули,
но и на весь проект.
|
|
18-03-2003 22:44сообщение от автора материала to Sunny:
Кстати да, действительно. Я не обратил на это внимания сразу только потому, что не имею привычки заводить глобальные переменные без инициализации. Кстати, начальная инициализация переменных заставляет компилятор вставлять код финализации. =8-|
То есть, если написать
var
i : integer = nil;
то все будет работать так, как должно.
Так вот и родился ещё один путь обхода проблемы. :o)
P.S. На днях запостил описание бага в Quality Central. Бум посмотреть чем это кончится...
|
|
17-03-2003 14:57У меня такая-же ситуация получилась и в модуле:
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils,
Unit1 in "Unit1.pas";
begin
end.
//////////////// Unit1 //////////////////
unit Unit1;
interface
implementation
type
TSomeObject = class(TInterfacedObject)
constructor Create;
destructor Destroy; override;
end;
{ TSomeObject }
constructor TSomeObject.Create;
begin
writeln("Created");
end;
destructor TSomeObject.Destroy;
begin
writeln("Destroyed");
inherited;
end;
procedure T1;
var
i : IUnknown;
begin
i := TSomeObject.Create;
end;
var
i : IUnknown;
initialization
i := TSomeObject.Create;
T1;
end.
Уничтожился только объект в процедуре T1.
|
|
13-03-2003 19:40сообщение от автора материала >>>Где он это делает?
Help читайте. Там написано в каких случаях что происходит. В основном счетчики ссылок щелкают при передаче параметров, присвоении и выходе из области видимости. Все достаточно просто. |
|
13-03-2003 19:10Да? А если я вызываю сторонний COM сервис. Он сам считает ссылки на себя, но и компилятор Delphi считает свои. Где он это делает? Я бы хотел видеть момент вызова _Release и _AddRef. А циклические ссылки? |
|
13-03-2003 14:24сообщение от автора материала to MAD:
>>>Но там я могу посмотреть код, который считает ссылки.
В Delphi тоже. См. методы _AddRef и _Release в TInterfacedObject. |
|
13-03-2003 14:11сообщение от автора материала to MAD:
>>>Но там я могу посмотреть код, который считает ссылки.
В Delphi тоже. См. методы _AddRef и _Release в TInterfacedObject. |
|
13-03-2003 14:04А String ... не скажу что char* лучше, тут вы правы :-))) |
|
13-03-2003 14:02To: Алексей Вуколов
Про смарт указатели я знаю. Но там я могу посмотреть код, который считает ссылки. В Delphi же я не могу этого увидеть - это вшито в компилятор. Вот это лажа. Язык и библиотека реализаций должны как мне кажется чётко разделены. |
|
13-03-2003 11:38сообщение от автора материала to Karbazol:
>>>Объект в такой ситуации не создается, а значит и разрушать нечего ;-)
Извините, но это чушь. Вы код-то получившийся смотрели или как?
to MAD:
>>>Я пересел с С++ на паскаль. И концепция закадрового подсчета ссылок меня несколько напрягает.
Ну если с C++, то про автоматическое разрушение экземпляров классов по выходу из области видимости а также про smart-указатели для COM объектов Вы знать должны. Здесь та же самая по смыслу техника используется.
Кстати, концепция подсчета ссылок для string не напрягает?
>>>А так как есть - лажа.
Лажа получается только в одном месте. В каком - я ясно показал. В остальных случаях это все прекрасно работает. Я интерфейсами достаточно активно пользуюсь во многих проектах.
|
|
13-03-2003 09:45Цитату привести не могу. Но такой совет я когда то запомнил. Вообще я всегда использовал функциональное замыкание - для объекта
var
SomeObject:TSomeObject;
begin
SomeObject:=nil;
try
SomeObject:=TSomeObject.Create;
//тут чего-то делаем
SomeObject.SomeMethod;
finally
FreeAndNil(SomeObject);
end;
end;
по аналогии для интерфейса
var
SomeRef:ISameIntf;
begin
SomeRef:=nil;
try
SomeRef:= TSomeObject.Create;
SomeRef.SomeMethod;
finally
SomeRef := nil;
end;
end;
Я пересел с С++ на паскаль. И концепция закадрового подсчета ссылок меня несколько напрягает. Если бы это было опциональным - это одно. Была бы полезная фича, уменьшающая кол-во ошибок и дающая нужную гибкость. А так как есть - лажа.
|
|
13-03-2003 03:34//А как вам такой вариант?
//Объект в такой ситуации не создается, а значит и разрушать нечего ;-)
//Это не камень, а оптимизация
program Stone;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
TSomeObject = class(TInterfacedObject)
constructor Create;
destructor Destroy; override;
end;
{ TSomeObject }
constructor TSomeObject.Create;
begin
writeln("Created"); //<- сюда управление не попадает
end;
destructor TSomeObject.Destroy;
begin
writeln("destroyed"); //<- сюда управление не попадает
inherited;
end;
var
i : IUnknown;
begin
i := TSomeObject.Create;
end.
|
|
12-03-2003 18:53сообщение от автора материала to MAD:
>Как кстати и положено делать прилежным мальчикам
Пожалуйста приведите цитату из документации, где написано, что так делать необходимо. В свою очередь, цитату, в которой говорится, что интерфейсные ссылки финализируются при выходе из области видимости привожу:
Interface references are managed through reference-counting, which depends on the _AddRef and _Release methods inherited from IInterface. When an object is referenced only through interfaces, there is no need to destroy it manually; the object is automatically destroyed when the last reference to it goes out of scope.
А вообще, прилежным мальчикам положено делать компилятор в соответствии со своей же спецификацией. И работать должно так, как положено. А положено делать финализацию.
>Устанавливайте явно ссылки в nil не полагаясь на компилятор.
Оно, конечно, да, только баг от этого багом не перестает быть.
|
|
12-03-2003 18:34сообщение от автора материала to MAD:
>Как кстати и положено делать прилежным мальчикам
Пожалуйста приведите цитату из документации, где написано, что так делать необходимо. В свою очередь, цитату, в которой говорится, что интерфейсные ссылки финализируются при выходе из области видимости привожу:
Interface references are managed through reference-counting, which depends on the _AddRef and _Release methods inherited from IInterface. When an object is referenced only through interfaces, there is no need to destroy it manually; the object is automatically destroyed when the last reference to it goes out of scope.
А вообще, прилежным мальчикам положено делать компилятор в соответствии со своей же спецификацией. И работать должно так, как положено. А положено делать финализацию.
>Устанавливайте явно ссылки в nil не полагаясь на компилятор.
Оно, конечно, да, только баг от этого багом не перестает быть.
|
|
12-03-2003 16:36Не совсем страшно.
И типовое решение - вот такое
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils,Dialogs;
type
TSomeObject = class(TInterfacedObject,IInterface)
destructor Destroy; override;
end;
{ TSomeObject }
destructor TSomeObject.Destroy;
begin
WriteLn("Òèïà äåñòðîé");
inherited;
end;
var
i:IInterface;
begin
i:=TSomeObject.Create;
i:=nil; //Явно выставим ссылку в nil. Как кстати и положено
// делать прилежным мальчикам. :-)
Readln;
end.
Устанавливайте явно ссылки в nil не полагаясь на компилятор. А вообще этот механизм с закадровым колдовством компилятора плохо пахнет. |
|
|
|