Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 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 ;)



Страницы: 1 вся ветка

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

Наверх





Память: 0.54 MB
Время: 0.005 c
3-8778
Сержик
2002-01-04 22:47
2002.02.04
Можно запрос делать сразу из двух баз?


7-9017
TheNobody
2001-10-06 04:24
2002.02.04
(работа со звуком) Существует ли технология...


1-8810
Анатолий
2002-01-18 03:49
2002.02.04
SaveDialog


7-9008
Vitaliy Jungle
2001-10-13 05:42
2002.02.04
Гребаный MSDOS


14-8977
evgeg
2001-12-12 21:27
2002.02.04
А вы знаете, что ссылки на этот форум есть на сайт www.rsdn.ru в разделе





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский