Форум: "Основная";
Поиск по всему сайту: delphimaster.net;
Текущий архив: 2002.02.04;
Скачать: [xml.tar.bz2];




Вниз

Динамические объекты 


Turalyon   (2002-01-15 09:44) [0]

Привет, Всем.

Есть такая проблемма при работе с динамическими объектами. Есть некая форма на которой в зависимости от действий пользователя создаются несколько кнопочек (TSpeedButton для удобства). Прежде чем их создавать я уничтожаю уже созданные ранее делаю все примерно так.

NumButton - количество созданых кнопок.
NewNumButton - колчество кнопок, которые надо сделать.


Var
BA : array [1..50] of TSpeedButton;
...

for i := 1 to NumButton do (BA[i] as TSpeedButton).Free;

...

for i := 1 to NewNumButton do
begin
BA[i] := TSpeedButton.Create(nil)
...
BA[i].Parent := Form1;
end;



Так вот проблемма в том что прога без конца вылетает с "Access violation at adress ...". Причем без какой либо видимой закономерности... то все делает нормально, то глючит. (Хотя небольшая есть - если новое количество кнопок меньше старого то вероятность больше). Если убрать сточку где удаляются старые кнопки - то все работает. Помогите плз...



MBo   (2002-01-15 09:50) [1]

скорее всего, ты пытаешься уничтожить больше кнопок, чем создавал- подозрения есть, т.к. счетчики разные -NumButton и NewNumButton.
И ни к чему приводить тип- у тебя же и так
array[] of TSpeedButton



Turalyon   (2002-01-15 09:56) [2]

Забыл дописать строку кода перед циклом for

NumButton := NewNumButton;

А вообще в хелпе написано, что метод Free работает корректно даже если объект - nil в смысле не создан, а вот Destroy ругается. Я пробовал и с деструктором работать - та же петрушка...

А про массив я не понял - что ты хотел сказать???



panov   (2002-01-15 10:05) [3]

"Free работает корректно даже если объект - nil"
А с чего ты взял, что у тебя ссылка на объект nil?

Var
BA : array [1..50] of TSpeedButton;
...

for i := 1 to NumButton do
begin
if Assigned(BA[i]) then TSpeedButton(BA[i]).Free;
BA[i] := nil;
end;

...

NumButton := NewNumButton;

for i := 1 to NewNumButton do
begin
BA[i] := TSpeedButton.Create(nil)
...
BA[i].Parent := Form1;
end;





MBo   (2002-01-15 10:09) [4]

>про массив я не понял - что ты хотел сказать
что BA[i] - уже объект типа TSpeedButton



Владислав   (2002-01-15 10:24) [5]

Уничтожение:
for i:=ComponentCount-1 downto 0 do
if Components[i] is TSpeedButton then
TSpeedButton(Components[i]).Free



Владислав   (2002-01-15 10:27) [6]

Создание:
for i:=0 to NewNumButton do
with TSpeedButton.Create(self) do
Parent:=Self



Turalyon   (2002-01-15 10:33) [7]

2MBo
это я просто уже как только не извращялся пытаясь грабли найти... думал, что может она некорректно понимает тип объекта, по этому его удаляет криво - вот и поставил явное указание.

2Panov
Все равно хандрит... но у меня появилось ощущение, что не это не совсем там...

Может такое быть, что он не успев их всех удалить начинает создавать новые????



Alx2   (2002-01-15 10:45) [8]

Код бы поподробнее посмотреть...



Turalyon   (2002-01-15 10:48) [9]

Она ошибку выдает, когда уже вся процедура закончилась, уже ничего делать не должна, в смысле должна ждать от пользователя его действий...



Владислав   (2002-01-15 10:51) [10]

> Turalyon © (15.01.02 10:48)
Зачем ты используешь собственный массив?
Это уже реализовано в VCL.



Alx2   (2002-01-15 10:53) [11]

Дело в том, что нагадить прога могла раньше. А аукается сейчас...
Если в параметр конструктору Create передавать не Nil, а, например, Self, или кого-то еще, то уничтожать эти батоны будет Self в собственном деструкторе, или кто-то еще (если не перекроешь, конечно).



Владислав   (2002-01-15 10:57) [12]

> Alx2 © (15.01.02 10:53)

то уничтожать эти батоны будет Self в собственном деструкторе

Так оно и замечательно! Не нужно ничего своего изобретать! Уже все есть!



Turalyon   (2002-01-15 10:58) [13]

2 Владислав
Поподробнее пожалуста. Просто тот способ, который я реализовал был первый пример который я нашел... Я раньше не работал с динамическими объектами.



Turalyon   (2002-01-15 11:07) [14]

2 Alx2
Попробовал переделать параметр Create на Self то же самое.
А код очень громоздкий... боюсь его никто и читать не возмется...



Alx2   (2002-01-15 11:12) [15]

>Владислав ©
Конечно. Но все дело в специфике задачи. Поэтому надо код...
Иначе будем гадать по методу черного ящика. Remote debug, так сказать...



Turalyon   (2002-01-15 11:22) [16]

ОК.

Вот процедура в которй все варится


procedure TEkzamForm.UpdateAnswer;
var Answ :array [1..2] of array of integer;
CountP, k,i : integer;
SetCif : set of 1..255;
tmpStr : String;
begin
DM.EAnswerQuery.DisableControls;
if DM.EAnswerQuery.State = dsBrowse then
for i := 1 to AnswNum do
begin
if Assigned(ButtonArray[i]) then ButtonArray[i].Free;
ButtonArray[i] := nil;
// ButtonArray[i].Free;
end;

В общем вот весь процесс удаления кнопок, проверка стоит на случай первого токрытия формы, тогда массив еще пустой, ButtonArray - глобальный массив
все что идет дальше - динамичесое построение SQL запроса.


DM.EAnswerQuery.SQL.Clear;
DM.EAnswerQuery.SQL.Add("select * from Answer");
DM.EAnswerQuery.SQL.Add("where A_Quest = " + DM.EQuestQuery.FieldByName("Q_id").AsString);

if Options.CheckBox3.Checked then
begin
SetCif := [];
CountP := 0;
SetLength(Answ[1], DM.EAnswerQuery.RecordCount);
SetLength(Answ[2], DM.EAnswerQuery.RecordCount);
repeat
k := random( length(Answ[1]) + 1);
if(not (k in SetCif))and(k <> 0) then
begin
SetCif:=SetCif +[k];
Answ[2,CountP] := k;
inc(CountP);
end;
until CountP = length(Answ[1]);

for k := 0 to length(Answ[1])-1 do
begin
DM.EAnswerQuery.RecNo := Answ[2,k];
Answ[1,k] := DM.EAnswerQuery.FieldByName("A_id").AsInteger;
end;
DM.EAnswerQuery.Close;
DM.EAnswerQuery.SQL.Clear;
DM.EAnswerQuery.SQL.Add("select * from Answer");
DM.EAnswerQuery.SQL.Add("where A_Quest = " + DM.EQuestQuery.FieldByName("Q_id").AsString);

tmpStr := "and A_id in (";
i:=0;
repeat
tmpStr := tmpStr + inttostr(Answ[1,i])+",";
inc(i);
until i=length(Answ[1])-1;
tmpStr := tmpStr + inttostr(Answ[1,i])+")";
DM.EAnswerQuery.SQL.Add(tmpStr);
end;

DM.EAnswerQuery.Open;



А здесь начинается создание новых кнопочек



AnswNum := DM.EAnswerQuery.RecordCount;
for i := 1 to AnswNum do
begin
ButtonArray[i] := TSpeedButton.Create(Self);
ButtonArray[i].Caption := inttostr(i);
ButtonArray[i].Width := 40;
ButtonArray[i].Height := 25;
ButtonArray[i].Left := 1 + 40*i ;
ButtonArray[i].Top := 18;
ButtonArray[i].Parent := GroupBox2;
ButtonArray[i].OnClick := OnMyButtonClick;

if Options.ComboBox2.ItemIndex = 1 then
begin
Memo1.Lines.Add("");
Memo1.Lines.Add("



Alx2   (2002-01-15 11:42) [17]

Описание/инициализация массива ButtonArray нужны.

На всякий случай: AnswNum всегда совпадает при создании/удалении кнопочек?



Digitman   (2002-01-15 11:50) [18]

Где у тебя код инициализации глобального массива ButtonArray ?
Перед первым использованием все его элементы д.б. сброшены в nil.

AV возникает у тебя при выполнении строчки

if Assigned(ButtonArray[i]) then ButtonArray[i].Free;


потому как массив, скорее всего, у тебя распределен статически, а компилятор не отвечает за инициализацию стат.массивов.
Ты же , видимо, ошибочно предполагаешь, что при первом выполнении проверки Assigned(ButtonArray[i]) она обязательно даст False, поскольку кнопок на этот момент ты никаких еще не создавал (и, соответственно, Free не будет выполняться).
Но Free может в какой-то момент и выполнится, потому что содержимое явно неинициализированного стат.распр.массива попросту неопределено (нет никакой гарантии, что все его элементы - nil)

Правильней (и красивей, imho) надо бы "нарисовать" так :

код предв.инициализации массива (его можно разместить, к примеру, в разделе иниц-ции модуля формы) :

for i := Low(ButtonArray) to High(ButtonArray) do
ButtonArray[i]:= nil;


и тогда цикл гарантированного разрушения потенциально созданных объектов в массиве будет выглядеть так:


if DM.EAnswerQuery.State = dsBrowse then
for i := 1 to AnswNum do
// уничтожить обьъект, если он на этот момент существует,
// и "убрать за собой мусор" во избежание последующих AV,
FreeAndNil(ButtonArray[i]);



Turalyon   (2002-01-15 11:53) [19]

Var

ButtonArray : array[1..50] of TSpeedButton;

AnswNum - глобальная переменная, нигде кроме этой процедуры не используется, задается перед началом инициализации новых кнопочек.



Владислав   (2002-01-15 11:53) [20]

> Turalyon © (15.01.02 10:58)
Подробнее см.:
Владислав © (15.01.02 10:24)
Владислав © (15.01.02 10:27)
Задавай все свойства в этом коде.
Там где ты создаешь компоненты (TSpeedButton), вставь код, который я назвал "создание". Там где уничтожаешь - "уничтожение".
Что не понятно? Конкретнее.



GorA   (2002-01-15 11:54) [21]

попробуй создавать кнопки так:

while not DM.EAnswerQuery.Eof do
begin
with TSpeedButton.Create(GroupBox2) do
begin
Caption := ...;
......
Paren := GroupBox2;
end;
DM.EAnswerQuery.Next
end;


а удалять вот так:

for i = GroupBox2.ComponentCount-1 downto 0 do
if GroupBox2.Components[i] is TSpeedButton then
GroupBox2.Components[i].Free;


и не нужны тебе никакие массивы



Alx2   (2002-01-15 11:58) [22]

Согласен с GorA.

Возвращаесь к конкретной задаче:
По поводу ButtonArray:
1. Сначала надо занулить его (см Digitman)
2. Где гарантия, что 0<AnswNum<51?



Turalyon   (2002-01-15 12:02) [23]

Дело в том что при первом запуске DM.EAnswerQuery.State стабильно равна dsInactive и первый раз цикл выполнятся не должон.



Turalyon   (2002-01-15 12:04) [24]

2 GorA
Сейчас попробую...



Digitman   (2002-01-15 12:23) [25]

>Turalyon (15.01.02 12:02)
Как бы там ни было, но исключения AV, которые ты получаешь, происходят именно в момент выполнения ButtonArray[i].Free в момент, когда ButtonArray[i] <> nil и указывает на несуществующий уже (или - еще) объект. И это выясняется очень просто установкой breakpoint"а на эту строчку (либо использовав адрес, который сопровождает возникшее исключение, для локализации в тексте проблемной строчки). То, что ты до сих пор мучаешься в поисках источника такой простейшей проблемы, лишний раз должно заставить тебя изучить и задействовать всю мощь интегрированного отладчика Делфи. И неважно при этом, какие там методы распределения памяти используешь - динамические или статические : дебаггер одинаково хорошо позволяет локализовать подобные источники ошибок в том и другом случаях.



Alx2   (2002-01-15 12:29) [26]

>Digitman
происходят именно в момент выполнения ButtonArray[i].Free в момент, когда ButtonArray[i] <> nil и указывает на несуществующий уже (или - еще) объект.
Ага. Но такое же может случиться при записи в ButtonArray[51], например, при отключенном Range Checking.



Digitman   (2002-01-15 12:32) [27]

>Alx2
Это из той же оперы - тот же AV. Локализуется точно так же.



Alx2   (2002-01-15 12:42) [28]

>Digitman
Конечно, локализуется. Но в том то и петрушка, чтобы вычислить причину AV "на расстоянии", по методу "вопрос-ответ", как в этом случае.



Turalyon   (2002-01-15 14:33) [29]

>Digitman
Я обчно пользуюсь Watch-ем Trace, ... и т.д.
Но вся проблемма что Trace проходит все нормаьлно и ошибки выдает уже не на выходе....
Как определить ошбку по адресу? Подскажите пожалуйста.

Я переделал все немного, и начал делать так, как написал GorA (без массива), но прога все равно вываливается. А удалял кнопки методом GroupBox2.DestroyComponents , т.к. кроме временных кнопок там больше ничего нет.



panov   (2002-01-15 14:38) [30]

У тебя в коде написано ButtonArray[i] := TSpeedButton.Create(Self);
Это так и нужно?
После этой строки владельцем кнопки будет форма.
А если они у тебя динамически создаются и уничтожаются, то, мне кажется, лучше все-таки TSpeedButton.Create(nil);



Владислав   (2002-01-15 14:39) [31]

Определяй где ошибка. Меню Search->Find Error...



Alx2   (2002-01-15 14:48) [32]

> Turalyon ©
Так мы, кажется, и не двинулись с мертвой точки...
Намыль мне unit. Покажу на подозрительные места, если что...



Digitman   (2002-01-15 15:31) [33]

>Turalyon
"На выходе" - это надо понимать - при выполнениии в процессе трассировки оперетора END, завершающего процедуру ? Вопрос -какой процедуры ? В этом случае практически 100%-но можно утверждать, что процедура портит либо стек либо область памяти, ей не принадлежащей по логике.
Воспользуйся советом от <Владислав>, это - основополагающий элемент умения пользоваться отладчиком для поиска простых лог.ошибок



Turalyon   (2002-01-16 11:26) [34]

Спасибо всем откликнувшимся за помощь, особенно Alx2 ;)




Форум: "Основная";
Поиск по всему сайту: delphimaster.net;
Текущий архив: 2002.02.04;
Скачать: [xml.tar.bz2];




Наверх





Память: 0.79 MB
Время: 0.033 c
14-8995           Tosov                 2001-12-16 00:24  2002.02.04  
Как правильно: реестр или регистри


3-8793            Flagman               2002-01-07 04:44  2002.02.04  
Locate + CaseSensetive? HELP!!!


1-8892            szap                  2002-01-15 19:32  2002.02.04  
Какая дата была была 5 дней назад?


3-8760            weak                  2002-01-04 12:48  2002.02.04  
Dataset not in edit or insert mode


1-8834            Ajax                  2002-01-15 09:31  2002.02.04  
Изменение свойств KOL формы