Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2013.03.22;
Скачать: CL | DM;

Вниз

Почему 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]

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

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



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

Текущий архив: 2013.03.22;
Скачать: CL | DM;

Наверх




Память: 0.57 MB
Время: 0.112 c
2-1336423671
efgen
2012-05-08 00:47
2013.03.22
Решение нелинейных уравнений в Delphi


2-1333010150
rodionov_uv
2012-03-29 12:35
2013.03.22
Помогите организовать поиск и удаление строки в Excele


15-1353960706
Дмитрий С
2012-11-27 00:11
2013.03.22
Удаленная отладка Lazarus


15-1341001802
Юрий
2012-06-30 00:30
2013.03.22
С днем рождения ! 30 июня 2012 суббота


15-1344957186
Рекрут
2012-08-14 19:13
2013.03.22
Безопасность Веб-Мани