Форум: "Основная";
Текущий архив: 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