Главная страница
    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]

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

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



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

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

Наверх





Память: 0.54 MB
Время: 0.058 c
15-1329149862
Alex555
2012-02-13 20:17
2013.03.22
Можно ли как-то посмотреть вот это видео


15-1344972507
guest
2012-08-14 23:28
2013.03.22
conhost.exe...


2-1329811084
leklerk
2012-02-21 11:58
2013.03.22
Изменение данных в БД через DBGrid


2-1345111451
Дмитрий Белькевич
2012-08-16 14:04
2013.03.22
Найти вернее окно (форму) среди нескольких одинаковых


2-1333196241
SKIPtr
2012-03-31 16:17
2013.03.22
управление входящим подключением





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