Текущий архив: 2004.08.29;
Скачать: CL | DM;
Вниз
Dispose Найти похожие ветки
← →
TUser © (2004-08-14 10:36) [0]Пол-ночи просидел, никак не могу понять, где ошибка в таком коде
pcat:=FCatalog;
while pcat <> nil do begin
pcat1:=pcat^.Next;
pcl:=pcat^.First;
while pcl <> nil do begin
pcl1:=pcl^.Next;
pch:=pcl^.First;
while pch <> nil do begin
pch1:=pch^.Next;
try
if pch^.AutoFree then // begin
// if assigned(pch^.Control) then
FreeAndNil(pch^.Control)
{end} else pch^.Control:=nil;
except
// if Control is alreadey destroyed by user
end;
Dispose(pch);
pch:=pch1;
end;
if assigned(pcl^.Font) then
FreeAndNil(pcl^.Font);
Dispose(pcl);
pcl:=pcl1;
end;
Dispose(pcat);
pcat:=pcat1;
end;
if assigned(FList) then
FList.Free;
Поясню. Есть дерево из нескольких уровней, корень его - FCatalog. Я совершаю его обход и освобождаю занятую память. Там, где есть ссылки на объекты - их я дестрою. То, что память выделена, все ссылки присвоены правильно, если нет ничего - то nil, и дерево построено правильно и т.д. - это я говорю совершенно точно и уверен на 100%. С этим деревом программа работает и до тех пор, пока не начнет выполняться этот код все работает нормально. Короче говоря, нет у меня подозрений на какую-то тривиальную ошибку.
А этот код выполняется нестабильно. Иногда выполняется, иногда при возникает AV. Причем даже так - подряд какое-то количество раз все выполняется нормально, а потом вдруг начинается глюк. Много раз подряд глючит, потом опять все нормально.
Отладчик говорит, что ошибки возникают на Dispose"ах. При этом Ctrl+F7 показывает, что соотвествующий указатель куда-то указывает и там действительно лежит некоторое (причем правильное) значение. Т.е. вроде бы попытка высвободить эту память не должна привождить к ошибкам.
В чем тут может быть дело.
← →
Sergey Kaminski © (2004-08-14 12:12) [1]ИМХО, пытаясь анализировать _такой_ код, можно потратить пол-ГОДА.
Кажется, имеет смысл сделать обход дерева не столь причудливым образом, а просто, именно - используя рекурсивный алгоритм.
А то ведь можна рехнуться в этих pcat,pcat1,pch,pch1,pcl...я серъёзно :( они ведь еще внутриwhile
неоднократно замословатейшим образом изменяются, тут не уследишь...
Удачи.
← →
Sergey Kaminski © (2004-08-14 12:22) [2]Например, что мещает сделать вот таким образом (пример, разумеется, схематический):
procedure DeleteRecursively (ASubNode: TMyNode);
var
I: Integer;
begin
for I := 0 to ASubNode.Count-1 do
begin
DeleteRecursively (ASubNode[I]);
end;
ASubNode := nil;
end;
begin
DeleteRecursively (FCatalog); // это все :)
end.
going down recursively.... (c)
:-)
← →
TUser © (2004-08-14 12:42) [3]Мешает то, что на разных уровнях дерева узлы разные. Например на уровне pcl расположены PCell = ^TCell, а одним из элементов этого TCell является TFont, которого нет на других уровнях. А на самом нижнем уровне есть TControl, которого на более высоких уровнях нет. Ну и т.д.
А обход дерева таким способом - это вполне нормальное дело, тут проблема не в алгоритме обхода, а с дестроем объектов и работе с памятью. Только вот я никак не могу найти этот баг.
← →
Romkin © (2004-08-14 12:58) [4]А о полиморфизме слышали?
Если объекты правильные - какая разница, что они разные?
← →
Romkin © (2004-08-14 13:01) [5]Sergey Kaminski © (14.08.04 12:22) [2]
procedure DeleteRecursively (ASubNode: IMyNode);
;)
Тогда все верно
← →
KSergey © (2004-08-14 13:02) [6]Я понимаю, что это будет решением не буквально текущей проблемы, но может подход просто поменять? Т.е. хранить в каждом узле потомки одного объекта, а уж объект пусть сам и уничтожает грамотно всю информацию о своих внутренностях?
Тогда и не будет проблемы вроде "на уровне pcl расположены PCell = ^TCell, а одним из элементов этого TCell является TFont, которого нет на других уровнях. А на самом нижнем уровне есть TControl, которого на более высоких уровнях нет."
> тут проблема не в алгоритме обхода, а с дестроем объектов
> и работе с памятью.
Вы уверены? А просто создайте и отдестройте - проблемы есть? А если нет - тогда, видимо, не в этом и дело ;)
А вообще, конечно, что-то понять тут очень сложно, тем более, что не понятно, как все это создавалось.
Вот только код создания - есть ли смысл приводить.. Слишком все запутано ;) ТОлько личные героические усилия тут спасут ;)
А так же минимальные демо-примеры, где возникают такие же проблемы, но гда можно разобраться за разумное время, в отличии от вышеприведенного кода.
← →
Sergey Kaminski © (2004-08-14 13:06) [7]Romkin © (14.08.04 13:01) [5]
Это...дело того...наживное!
;)
← →
TUser © (2004-08-14 14:16) [8]
> KSergey © (14.08.04 13:02) [6]
Да, возможно вы правы. Хотя, создавать отдельный объект для каждого нода - не знаю, насколько это рационально, учитывая, что объектов м.б. много. С записями работать и быстрее и меньше памяти надо.
Но не в том суть. Создаются они таким вот образом
PChild = ^TChild;
TChild = record
Next:PChild;
Control:TControl;
...
end;
PCell = ^TCell;
TCell = record
Next:PCell;
First:PChild;
Col, Row:integer;
...
end;
PCatalog = ^TCatalog;
TCatalog = record
Next:PCatalog;
First:PCell;
Data:TDataType;
end;
procedure TMyStringGrid.GetCellEx(ACol, ARow:integer; var P:PCell);
var pcat:PCatalog;
data:TDataType;
f:boolean;
begin
data:=CalculateData(ACol,ARow);
pcat:=FCatalog;
f:=true;
while f and (pcat <> nil) do
if pcat^.Data <> data then
pcat:=pcat^.Next
else f:=false;
if f then begin
New(pcat);
pcat^.Next:=FCatalog;
FCatalog:=pcat;
pcat^.First:=nil;
pcat^.Data:=data;
end else f:=true;
P:=pcat^.First;
while f and (P <> nil) do
if (P^.Col = ACol) and
(P^.Row = ARow) then
f:=false
else P:=P^.Next;
if f then begin
New(P);
P^.Next:=pcat^.First;
pcat^.First:=P;
P^.First:=nil;
присваиваем значения
end;
end;
...
GetCellEx(ACol,ARow,pcl);
New(pch);
pch^.Control:=AControl;
присваиваем значения
Где-то я я памятью напутал.
Я понимаю, что рабобрать код непросто. Опишу пократче. Грубо говоря, если у меня есть структура, например, такая
PStruct = ^TStruct;
TStruct = record
Next:PStruct;
Control:TControl;
end;
Я ее создаю вот так
var P:PStruct;
New (P);
Button1:=TButton.Create(Application);
P^.Control:=Button1;
А потом удаляю
FreeAndNil(P^.Control);
Dispose(P);
то вроде бы никаких проблем быть не должно. И, помимо прочего, от того вызываем мы Free для P^.Control или ен вызываем - от этого AV не должен проявляться. Так или не так?
Кстати, баг проявляется только, когда код выполняется в деструкторе объекта. Если тот же код выполняется где-то в рередине работы, то все нормально. Я тут думал - объекты, которые в P^.Control прописаны, у них owner могцу ставить nil или Application, но в любом случае баг есть. Но м.б. их уже нет к тому моменту, когда я пытаются их уничтожить. Хотя - там ведь специально для этого стоит try ... except.
Иными словами код
try
if pch^.AutoFree then // begin
// if assigned(pch^.Control) then
FreeAndNil(pch^.Control)
{end} else pch^.Control:=nil;
except
// if Control is alreadey destroyed by user
end;
Dispose(pch);
должен выполняться без бага, вне зависимости от того, существует pch^.Control или нет. Ведь так?
← →
Sergey Kaminski © (2004-08-14 14:31) [9]>>вне зависимости от того, существует pch^.Control или нет
То есть, вы полагаете, что если вызвать Free для несозданного объекта, то AV быть "не должно"?
← →
TUser © (2004-08-14 14:37) [10]Там для отлова этого AV стоит try ... except (см. код выше). А на Dispose(pch) AV быть не должно.
← →
KSergey © (2004-08-14 14:39) [11]Модельный код - вроде верный
Но у вас явно там все не так на самом деле.
Особенно не ясен комментарий: "// if Control is alreadey destroyed by user"
Если он "destroyed by user" - то как именно?? Этого не видно.
Опять же, если он там дестройнут через FreeAndNil или с последующим присвоением nil после Free - то и в проверках этих хитрых смысла нет.
if pch^.AutoFree then // begin
// if assigned(pch^.Control) then
FreeAndNil(pch^.Control)
{end} else pch^.Control:=nil;
Зачем это понаворочено? Если не assigned - тогда там и так nil.
Хотя тут комментариями что-то странное вообще наворчено: if"а нет, а else - остался...
← →
Sergey Kaminski © (2004-08-14 14:39) [12]кстати,
С записями работать и быстрее и меньше памяти надо
весьма спорное утверждение
← →
TUser © (2004-08-14 14:42) [13]
> KSergey © (14.08.04 14:39) [11]
Это контролы, которые можно встраивать в StringGrid. Т.е. создаем Button, пишем - вставить в такую-то ячейку. Все вроде работает. А уж что там пользователь компонента сделает дальше с этим Button"ом - этого TMyStringGrid не знает. Поэтому - try ... except.
else там от другого if"а получается если закоментировано. От того, который на строчку выше.
← →
Sergey Kaminski © (2004-08-14 14:44) [14]>> А на Dispose(pch) AV быть не должно.
Почему это не должно, когда PCh ни на что не указывает, то как раз и должно
Не верите, проверьте
type
PChild = ^TChild;
TChild = record
Next:PChild;
Control:TControl;
end;
var
PCh: PChild;
begin
// сразу делаем Dispose, без выделения памяти под Pch
Dispose(PCh);
end;
:)
← →
jack128 © (2004-08-14 14:45) [15]
> try
> if pch^.AutoFree then // begin
> // if assigned(pch^.Control) then
> FreeAndNil(pch^.Control)
> {end} else pch^.Control:=nil;
> except
> // if Control is alreadey destroyed by user
> end;
это не код - это тихий ужас.
← →
TUser © (2004-08-14 14:47) [16]Нет. Память выделена. Зуб даю. См. выше.
Просто выделяется она в одной процедуре, а освобождается в другой.
Кстати, отладчик видит правильную ин-фу (ту, которая там должна быть), на том месте, куда указывает указатель. Так что в выделенную память еще и инфа записана.
← →
KSergey © (2004-08-14 14:50) [17]> [13] TUser © (14.08.04 14:42)
> Это контролы, которые можно встраивать в StringGrid. Т.е.
> создаем Button, пишем - вставить в такую-то ячейку. Все
> вроде работает. А уж что там пользователь компонента сделает
> дальше с этим Button"ом - этого TMyStringGrid не знает.
Нет, не верный подход. Вы создали - вы и уничтожайте. А если пользователь уничтожит сам (не известно как и, главное, зачем??) - это его проблемы. Тогда исключение - нормальная на то реакция. Зачем его глушить?
if pch^.AutoFree then
FreeAndNil(pch^.Control)
else
pch^.Control:=nil;
Ага, теперь вник. Только вот я ли должен был код причесывать? ;)
Но тут опять неувязочка идеологическая.
Хорошо, компонент создали, флажек AutoFree уст. в False - и что же мы видим?? Что ссылка на компонент получится утерянной!!! Это же ужас! Т.е. корректно уничтожить его уже невозможно.
Но ведь наш компонент его создал, почему же заботиться об уничтожении должен кто-то другой??
← →
KSergey © (2004-08-14 14:53) [18]> [16] TUser © (14.08.04 14:47)
> Кстати, отладчик видит правильную ин-фу (ту, которая там
> должна быть), на том месте, куда указывает указатель. Так
> что в выделенную память еще и инфа записана.
Это ничего не значит. Значит лишь, что содержимое памяти по этому адресу еще не затерто. Вот и все.
А впамяти всегда что-нибудь да написано, там не бывает неопределенных значений ;)
← →
TUser © (2004-08-14 14:58) [19]
> Хорошо, компонент создали, флажек AutoFree уст. в False
> - и что же мы видим?? Что ссылка на компонент получится
> утерянной!!! Это же ужас!
Нет. Я создал кнопку в гриде, грид убил, а кнопка мне еще нужна. Например для другого грида. По умолчанию AutoFree = true, в нормальной ситуации все встроенные объекты с уничтожением грида дестроятся. А если мы так не хотим - ставим false и по событию OnDeleteCell (есть там такое) делаем со свободным контролом все, что нам надо.
← →
KSergey © (2004-08-14 15:03) [20]> [19] TUser © (14.08.04 14:58)
> Нет. Я создал кнопку в гриде, грид убил, а кнопка мне еще
> нужна. Например для другого грида.
Грубая ошибка.
Создали этим гридом, для его нужд. Вот пусть с ним и умрет. Если другому гриду понадобится - то пусть он и создает.
Либо тогда вообще выпонит ьвсе это создание/уничтожение за рамки грида и его этой ерундой не путать.
Важно: где создали -там и прибили.
Иначе получим наблюдаемый бардак.
← →
TUser © (2004-08-14 15:13) [21]
> Либо тогда вообще выпонит ьвсе это создание/уничтожение
> за рамки грида
Оно вынесено за рамки грида. Его встраивают так
B:=TButton.Create();
MyStringGrid1.ParentIn(1,1,B);
← →
KSergey © (2004-08-14 15:32) [22]> [21] TUser © (14.08.04 15:13)
Как-то оно не очень, конечно...
А может класс передавать? И создавать объект указанного класса... А как не нужен - так и уничтожать...
А вообще - ну надуманная проблема, раз уж так все устроено. Раз уж просим грид уничтожить - ну тогда и сами не лезем. Или уж Сами - тогда к гриду не в претензии.
Посмотрите про InplaceEditor как реализовано - уже все придумано ;)
← →
TUser © (2004-08-15 01:11) [23]Все. Нашел ошибку, спасибо всем.
Страницы: 1 вся ветка
Текущий архив: 2004.08.29;
Скачать: CL | DM;
Память: 0.55 MB
Время: 0.022 c