Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 2013.03.22;
Скачать: [xml.tar.bz2];

Вниз

Почему FreeAndNil такой, какой он есть?   Найти похожие ветки 

 
Омлет ©   (2012-06-19 11:12) [0]

Почему ссылка на объект обниляется перед вызовом деструктора, а не после?

procedure FreeAndNil(var Obj);
var
 Temp: TObject;
begin
 Temp := TObject(Obj);
 Pointer(Obj) := nil;
 Temp.Free;
end;


Наступил на грабли.. В деструкторе объекта генерируется событие, в обработчике которого было обращение к этому объекту (не через Sender). А, поскольку ссылка на него уже стерта (FreeAndNil постарался), получаем AV.


 
Ega23 ©   (2012-06-19 11:16) [1]


> Наступил на грабли.. В деструкторе объекта генерируется
> событие, в обработчике которого было обращение к этому объекту


 TObject = class
 public
   .....
   procedure AfterConstruction; virtual;
   procedure BeforeDestruction; virtual;

?


 
Плохиш ©   (2012-06-19 11:23) [2]


>  В деструкторе объекта генерируется событие, в обработчике
> которого было обращение к этому объекту (не через Sender).
>

Класс не должен знать ничего о каких-то ссылках на свои экземпляры.


 
Омлет ©   (2012-06-19 11:26) [3]

> Ega23 ©   (19.06.12 11:16) [1]

В данном случае BeforeDestruction ничем не поможет.


 
Ega23 ©   (2012-06-19 11:35) [4]


unit Unit44;

interface

uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 Dialogs, StdCtrls;

type

 TMyClass = class (TObject)
 private
   FOnNotify: TNotifyEvent;
 published
 public
   destructor Destroy; override;
   procedure Foo;
   property OnNotify: TNotifyEvent read FOnNotify write FOnNotify;
 end;

 TForm44 = class(TForm)
   Button1: TButton;
   procedure Button1Click(Sender: TObject);
 private
   procedure Bar(Sender: TObject);
 public
   { Public declarations }
 end;

var
 Form44: TForm44;

implementation

{$R *.dfm}

{ TForm44 }

procedure TForm44.Bar(Sender: TObject);
begin
 if Sender is TMyClass then
   TMyClass(Sender).Foo;

end;

procedure TForm44.Button1Click(Sender: TObject);
var
 obj: TMyClass;
begin
 obj := TMyClass.Create;
 try
   obj.OnNotify := Bar;
 finally
   FreeAndNil(obj);
 end;
end;

{ TMyClass }

destructor TMyClass.Destroy;
begin
 if Assigned(FOnNotify) then
   FOnNotify(Self);
 inherited;
end;

procedure TMyClass.Foo;
begin
 ShowMessage("Foo");
end;

end.


 
Омлет ©   (2012-06-19 11:39) [5]

> Плохиш ©   (19.06.12 11:23) [2]
> Класс не должен знать ничего о каких-то ссылках на свои экземпляры.


Он и не знает. Код довольно безобидный:


// Обработчик события OnChangeConnectionEvent для fDBConnector
procedure TForm1.ChangeConnectionEvent(Sender: TObject);
begin
 if fDBConnector.ConnectionState = csDisconnected then // Тут AV, т.к. fDBConnector уже = nil
   AddToLog("Disconnected.");
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
 FreeAndNil(fConnector); // Получаем Free->Disconnect->ChangeConnectionEvent->AV
end;


Если напишем
 if TDBConnector(Sender).ConnectionState ...
проблема решится.

Но вопрос не в этом.


 
Омлет ©   (2012-06-19 11:44) [6]

> Ega23 ©   (19.06.12 11:35) [4]

Я знаю, как решить проблему (см. [5]). Вопрос озвучен в заголовке ветки.


 
Sha ©   (2012-06-19 11:56) [7]

Для большей распущенности?
Чтобы вложенный вызов Free через ту же переменную был безопасен?


 
Омлет ©   (2012-06-19 12:03) [8]

Должен быть какой-то веский смысл заводить доп. переменную вместо того, чтобы просто написать:

procedure FreeAndNil(var Obj);
begin
 Obj.Free;
 Obj := nil;
end;


 
CRLF   (2012-06-19 12:07) [9]


> Омлет ©   (19.06.12 12:03) [8]
Если Obj.Free по какой-то причине упадёт, то... продолжи мысль :-)


 
Омлет ©   (2012-06-19 12:07) [10]

> Sha ©   (19.06.12 11:56) [7]
> Чтобы вложенный вызов Free через ту же переменную был безопасен?


Как это?


 
Омлет ©   (2012-06-19 12:09) [11]

> CRLF   (19.06.12 12:07) [9]
> Если Obj.Free по какой-то причине упадёт, то... продолжи мысль :-)


Точно! Думаю, ты прав.


 
Дмитрий С ©   (2012-06-19 12:34) [12]


> procedure TForm1.FormDestroy(Sender: TObject);
> begin
>  FreeAndNil(fConnector); // Получаем Free->Disconnect->ChangeConnectionEvent-
> >AV
> end;

А почему нельзя просто fConnector.free ?


 
Дмитрий С ©   (2012-06-19 12:35) [13]


> // Обработчик события OnChangeConnectionEvent для fDBConnector
> procedure TForm1.ChangeConnectionEvent(Sender: TObject);
>
> begin
>  if fDBConnector.ConnectionState = csDisconnected then //
> Тут AV, т.к. fDBConnector уже = nil
>    AddToLog("Disconnected.");
> end;

procedure TForm1.ChangeConnectionEvent(Sender: TObject);
var fDBConnector:TDBConnector absolute Sender;
begin
if fDBConnector.ConnectionState = csDisconnected then // Тут AV, т.к. fDBConnector уже = nil
  AddToLog("Disconnected.");
end;


 
Anatoly Podgoretsky ©   (2012-06-19 12:51) [14]


> Омлет ©   (19.06.12 11:12)  

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


 
Омлет ©   (2012-06-19 12:52) [15]

> Дмитрий С

Да не спрашивал я, как избежать AV.
Ты уже четвертый, кто вопрос не читал.


 
Омлет ©   (2012-06-19 12:53) [16]


> Anatoly Podgoretsky ©   (19.06.12 12:51) [14]

Пятый.


 
Sha ©   (2012-06-19 13:01) [17]


> Омлет ©   (19.06.12 12:07) [10]
> > Sha ©   (19.06.12 11:56) [7]> Чтобы вложенный вызов Free
> через ту же переменную был безопасен?Как это?


Ну, если ты перекрыл Free, то теоретически есть шанс вызвать его еще раз из него же.


 
Омлет ©   (2012-06-19 13:11) [18]

> Sha ©   (19.06.12 13:01) [17]
> Ну, если ты перекрыл Free, то теоретически
> есть шанс вызвать его еще раз из него же.


Почему повторный вызов будет безопасен? Ведь AV же случится.


 
Давайте будем жрать!   (2012-06-19 13:13) [19]

Смотря как перекроешь.


 
Sha ©   (2012-06-19 13:14) [20]

Не случится.
Посмотри, что будет, при такой последовательности вызовов:
FreeAndNil->Free->Disconnect->FreeAndNil


 
Давайте будем жрать!   (2012-06-19 13:16) [21]


> Посмотри, что будет, при такой последовательности вызовов:
> FreeAndNil->Free->Disconnect->FreeAndNil
Есть мнение, что ничего хорошего. Фри — невиртуальный. ФриЭндНил вызовет ТОбжект.Фри, перекрытие пропадёт втуне.


 
Дмитрий С ©   (2012-06-19 13:24) [22]

Простите.


> Почему ссылка на объект обниляется перед вызовом деструктора,
>  а не после?

Потому что такая функция. Как раз вопрос в том, почему ты ее решил применить, и теперь жалуешься, что она тебе не подходит?

Кстати TObject(nil).Free() - не вызывает AV


 
Sha ©   (2012-06-19 13:28) [23]


> Давайте будем жрать!   (19.06.12 13:16) [21]
> > Посмотри, что будет, при такой последовательности вызовов:
> > FreeAndNil->Free->Disconnect->FreeAndNilЕсть мнение, что
> ничего хорошего. Фри — невиртуальный. ФриЭндНил вызовет
> ТОбжект.Фри, перекрытие пропадёт втуне.


Зато Destroy виртуальный.


 
Омлет ©   (2012-06-19 13:30) [24]

> Sha ©   (19.06.12 13:14) [20]

Теперь понял.

> Дмитрий С ©   (19.06.12 13:24) [22]
> Как раз вопрос в том, почему ты ее решил применить, и теперь
> жалуешься, что она тебе не подходит?


Мне всё подходит. Просто раньше не обращал внимания на реализацию этой процедуры - из её названия думал, что сначала Free, а потом Nil. Оказалось наоборот.


 
Давайте будем жрать!   (2012-06-19 13:31) [25]


> Sha ©   (19.06.12 13:28) [23]
Тогда к чему [17]?


 
Sha ©   (2012-06-19 13:33) [26]


> Давайте будем жрать!   (19.06.12 13:31) [25]
> > Sha ©   (19.06.12 13:28) [23]Тогда к чему [17]?


Виноват, был не совсем точен.


 
oxffff ©   (2012-06-19 14:22) [27]


> Омлет ©   (19.06.12 11:12) 
> Почему ссылка на объект обниляется перед вызовом деструктора,
>  а не после?
>
> procedure FreeAndNil(var Obj);
> var
>  Temp: TObject;
> begin
>  Temp := TObject(Obj);
>  Pointer(Obj) := nil;
>  Temp.Free;
> end;


Чтобы выявить цикл при разрушении. Поэтому стоит var.


 
oxffff ©   (2012-06-19 14:24) [28]


> Чтобы выявить цикл при разрушении.


В смысле не цикл разрушения. А цикл связности.


 
Anatoly Podgoretsky ©   (2012-06-19 14:37) [29]

> oxffff  (19.06.2012 14:22:27)  [27]

var что бы можно было вненею ссылку изменить.


 
robt   (2012-06-19 14:44) [30]


> Почему ссылка на объект обниляется перед вызовом деструктора,
>  а не после?

потомучто деструктор может выполняться оч долго и есть такая фигня, как многозадачность


 
oxffff ©   (2012-06-19 15:02) [31]


>  есть такая фигня, как многозадачность


Я плакалъ.


 
oxffff ©   (2012-06-19 15:03) [32]


> Anatoly Podgoretsky ©   (19.06.12 14:37) [29]
> > oxffff  (19.06.2012 14:22:27)  [27]
>
> var что бы можно было вненею ссылку изменить.


Не, чтобы кофе налить.


 
Cobalt ©   (2012-06-19 16:25) [33]

Если объект может быть обнулен, то if Assigned(obj) спасет отца русской демократии


 
DVM ©   (2012-06-19 16:38) [34]

Вот если кому интересно, тут про FreeAndNil кое-какие мысли:
http://www.gunsmoker.ru/2009/04/freeandnil-free.html


 
brother ©   (2012-06-19 16:46) [35]

перечитал, теперь поведение
> Почему ссылка на объект обниляется перед вызовом деструктора,
> а не после?

становится более понятным, впрочем, лично Я стараюсь придерживаться именно FreeAndNil...


 
Омлет ©   (2012-06-19 16:58) [36]

> DVM ©   (19.06.12 16:38) [34]
> http://www.gunsmoker.ru/2009/04/freeandnil-free.html


Спасибо. Т.е., с точки зрения GunSmoker-а объект обниляется перед Free, чтобы было легче вылавливать ошибки )
Это уже третий вариант ответа на мой вопрос.


 
Sha ©   (2012-06-19 17:05) [37]

Это только одна сторона медали. Одни ошибки вылавливаем, другие прячем.
Обе крайности вредны, хотя мне ближе другая )
Самому контролировать поведение своей программы как-то спокойнее.


 
brother ©   (2012-06-19 17:13) [38]

> Самому контролировать поведение своей программы как-то спокойнее.

а если код сложный?


 
brother ©   (2012-06-19 17:13) [39]

+ копи паст?


 
Sha ©   (2012-06-19 17:39) [40]

> а если код сложный?
> + копи паст?

ошибки надо исправлять, а не прятать


 
brother ©   (2012-06-19 18:56) [41]

> ошибки надо исправлять

вот FreeAndNil и поможет...


 
Sha ©   (2012-06-19 19:07) [42]

> вот FreeAndNil и поможет...

[37]


 
Владислав ©   (2012-06-19 23:21) [43]

Почему, если выстрелить себе в ногу, будет больно?..


> Наступил на грабли..


Вот чтобы было больно сразу после выстрела, а не через какое-то время, после прилетания черенка по голове.

Я к чему, помогло таки?


 
Омлет ©   (2012-06-19 23:55) [44]

> помогло таки?

Помогло.


 
Sha ©   (2012-06-20 00:31) [45]

попытки "обнулить переменную совсем" также "помогают" потерять контроль над программой


 
oxffff ©   (2012-06-20 00:34) [46]


> Sha ©   (20.06.12 00:31) [45]
> попытки "обнулить переменную совсем" также "помогают" потерять
> контроль над программой


Это как?


 
Sha ©   (2012-06-20 00:35) [47]

что как?


 
oxffff ©   (2012-06-20 00:38) [48]

Пример нужно.


 
Sha ©   (2012-06-20 00:45) [49]

Примера, как "обнулить переменную совсем", у меня нет. Была тут давно-давно веселая ветка, в которой ТС настойчиво хотел это сделать так, чтобы переменной не осталось вовсе, наверно. Думаю, многим она запомнилась.

Меня FreeAndNil настораживает не меньше, чем Application.ProcessMessages. Очень похоже, что у программиста была проблема, для решения которой использовались труднопонимаемые костыли. Скорее всего, все варианты передачи управления он не предусмотрел. Как-то так.


 
Petr V. Abramov ©   (2012-06-20 00:45) [50]


> oxffff ©   (20.06.12 00:38) [48]
>
> Пример нужно.
>

совсем :)


 
oxffff ©   (2012-06-20 00:50) [51]


> Sha ©   (20.06.12 00:45) [49]


Моя вина, я видимо что то пропустил. :)

P.S. Насколько помню, не использовал freeandnil
(2 раза не считается из 100500). Считаю тем не менее, что ее логика возможно кому то пригодится, и не отрицаю freeandnil. Но тем не менее, понимаю, что в сложных циклическим связках, гораздо лучше GC.


 
Anatoly Podgoretsky ©   (2012-06-20 07:38) [52]

> Sha  (20.06.2012 00:45:49)  [49]

Это у Борланда была проблема – чтобы еще для ламеров сделать, а то они
неумехи не могут переменную обнулить и вместо нормально решения в стиле ООП
они сделали нетипизированый костыль.


 
oxffff ©   (2012-06-20 09:12) [53]


> они сделали нетипизированый костыль.


Нетипизированный он и должен быть - это правильно с точки зрения теории типов.


 
KSergey ©   (2012-06-20 14:09) [54]

> oxffff ©   (20.06.12 09:12) [53]

Может таки TObject должен быть тип аргумента?
Иначе фигня получается.

Правда как это относится к теме данного топика - я чета не понимаю.


 
oxffff ©   (2012-06-20 15:11) [55]


> KSergey ©   (20.06.12 14:09) [54]
> > oxffff ©   (20.06.12 09:12) [53]
>
> Может таки TObject должен быть тип аргумента?
> Иначе фигня получается.


Фигня получается, если параметр будет TObject.

http://pages.cs.wisc.edu/~rkennedy/var-identical


 
KSergey ©   (2012-06-20 15:26) [56]

> oxffff ©   (20.06.12 15:11) [55]
> Фигня получается, если параметр будет TObject.

Можно поконкретнее, где там про фигню, относящуюся к FreeAndNil?
Впрочем, как и про любую другую.


 
oxffff ©   (2012-06-20 15:43) [57]


> KSergey ©   (20.06.12 15:26) [56]


Я не Rouse, на дискуссию у меня нет времени.


 
Kerk ©   (2012-06-20 15:46) [58]


> KSergey ©   (20.06.12 15:26) [56]

Попробуй сделать аналог FreeAndNil, принимающий TObject. Сам все поймешь.


 
oxffff ©   (2012-06-20 15:48) [59]


> Я не Rouse, на дискуссию у меня нет времени.


Впрочем думаю, что и у Александра его тоже нет.


 
jack128_   (2012-06-21 00:31) [60]


> Нетипизированный он и должен быть - это правильно с точки
> зрения теории типов.

Это правельно с точки зрения примитивной объект паскалевской системы типов. С дженериками эта процедура должна иметь такую сигнатуру:

procedure FreeAndNil<T:TObject>(var Obj: T);


 
icWasya ©   (2012-06-21 09:38) [61]

>Sha ©   (20.06.12 00:45) [49]
>была проблема, для решения которой использовались труднопонимаемые костыли.

Костыль применён, но им не воспользовались

procedure TForm1.ChangeConnectionEvent(Sender: TObject);
var fDBConnector:TDBConnector absolute Sender;
begin
 if Assigned(fDBConnector) then //<<==--
 AddToLog("Nul.") else
//<<==--
 if fDBConnector.ConnectionState = csDisconnected then
   AddToLog("Disconnected.");
end;


 
KSergey ©   (2012-06-21 10:46) [62]

> Kerk ©   (20.06.12 15:46) [58]

Короче наследника не дает запихать в var
Так бы и сказали. При чем тут дискуссия??


 
oxffff ©   (2012-06-21 12:56) [63]


> Это правельно с точки зрения примитивной объект паскалевской
> системы типов.


Я о том, что у типизированного указателя подтипом является только он сам.


 
wl ©   (2012-06-21 13:53) [64]

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


 
KSergey ©   (2012-06-21 14:14) [65]

> wl ©   (21.06.12 13:53) [64]

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

Этому ничего не мешает вообще, это все на совести программиста такие фокусы.


 
Kerk ©   (2012-06-21 15:17) [66]

В мелкософт есть один чувак (Реймонд Чен?), объясняющий в блоге истоки некоторых исторически сложившихся решений в Windows и в WinAPI вчастности. Интересно было бы подобное из недр Embarcadero почитать. Кто вообще придумал FreeAndNil и почему реализовал именно так :)


 
Anatoly Podgoretsky ©   (2012-06-21 15:31) [67]

> Kerk  (21.06.2012 15:17:06)  [66]

Раньше блоги назывались дискуссионные группы новостей, Было Borland News
Groups, там TEAMB обсуждали эту тему, в общем выходило что в основном для
ламеров, пошли на поводу, особенно сильно это было в Д6/7 они столько
функций наплодили, вместо развития языка



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

Форум: "Прочее";
Текущий архив: 2013.03.22;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.62 MB
Время: 0.07 c
15-1342038603
Юрий
2012-07-12 00:30
2013.03.22
С днем рождения ! 12 июля 2012 четверг


15-1345700788
oldman
2012-08-23 09:46
2013.03.22
И хваленый, пресловутый Фишер тут-же согласился на ничью...


15-1335549150
Влад
2012-04-27 21:52
2013.03.22
Сколько Калькуляторов получится открыть максимально?


15-1339959786
Artem
2012-06-17 23:03
2013.03.22
Pocket Fritz


15-1331806770
Musecd
2012-03-15 14:19
2013.03.22
Как в Delphi 2010 (2007-XE2) настроить цвет выделения кода ?





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
Английский Французский Немецкий Итальянский Португальский Русский Испанский