Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Проблема с TFileStream


Страницы:   1 2 3 4 5

ProgRAMmer Dimonych ©   (2007-11-05 22:45:00) 

Сталкивался много раз, сейчас решил разобраться :)

Дело в том, что TFileStream как-то странно себя ведёт. Даже, я бы сказал, непредсказуемо, т.е. не так, как должен был бы.

Например, в программе, которую я пишу сейчас используется вспомогательный модуль, содержащий объект для работы с файлами определённого формата. В некоторых его методах используется локальный

Tmp:TFileStream;

Забавно то, что в начале процедуры этот самый Tmp показывается (в Delphi'овом Hint'е) со свойством (Handle:12) и таким же остаётся после вызова его метода Free. То же самое с FileStream'ом, который блокирует файл до тех пор, пока он нужен моему объекту, и который объявлен как поле объекта.

Самое печальное, что, похоже, именно из-за такого поведения возникают AV и External Errors. Посмотрел исходник TFileStream'а и справку по Дельфям: в деструкторе TFileStream'а вызывается функция FileClose, которая, если верить справке, просто принимает Handle файла, что, на мой взгляд, имеет печальные последствия:

destructor TFileStream.Destroy;
begin
 if FHandle >= 0 then FileClose(FHandle);
 inherited Destroy;
end;


Т.е. FHandle передаётся не как var-параметр, а inherited Destroyтянется аж от TObject, где он virtual.

Попробовал копнуть в сети, но русскоязычного ничего не нашёл. Насколько мне позволяет судить моё знание английского, что-то по этой проблеме есть здесь: http://www.delphipraxis.net/topic77280.html.Если я правильно понимаю немецкий, то вместо метода Free следует юзать процедуру FreeAndNil().

Это так?


Anatoly Podgoretsky   (2007-11-06 18:57:00) [1]

> korneley  (06.11.2007 01:38:48)  [48]

> Начали с использования указателей на объекты, а закончили (?)

Не гони лошадей


Юрий Зотов   (2007-11-06 14:24:00) [2]

> homm ©   (06.11.07 12:16) [86]

Если подсчета ссылок нет, то FreeAndNil решает все проблемы. После этого вызова объект ТОЧНО уничтожен и об этом ОДНОЗНАЧНО сигнализирует FRef=nil.

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

> homm ©   (06.11.07 12:19) [87]
> Обычно этого и не требуется.


"Обычно" - это не "всегда". И если все же потребовалось, то как быть?

Пример такого "потребовалось" был приведен. Вопрос был задан. Ответа получено не было. Его и не могло быть получено. Потому что его нет.

==============

Это "удобство" - кажущееся. Потому что лишает программиста возможности самому решать, когда и что нужно делать.

Примерно как автомобиль, который дает водителю возможность крутить руль, но КОГДА начать поворачивать - решает сам. Обычно он решает правильно, но "обычно" - это не "всегда"...


homm   (2007-11-06 12:19:00) [3]

> но не дали способа узнать результат этого запроса.

Обычно этого и не требуется. На пример с TFileStream и хэндлом я ответил.


homm   (2007-11-06 12:16:00) [4]

> [85] Юрий Зотов ©   (06.11.07 12:06)
> Если в Method2 я просто напишу FRef.Free, то либо все пройдет
> нормально (объект еще существует), либо получим AV (объект
> уже был уничтожен и адрес, хранящийся в FRef стал недействительным).

[73] Вариант1.

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


Юрий Зотов   (2007-11-06 12:06:00) [5]

> homm ©   (06.11.07 11:26) [77]

> А для чего это может понадобиться, точно знать что объект убит?

А представьте себе, что Method2 - это деструктор объекта TMyObject. И в этом деструкторе надо освободить ресурсы - то есть, уничтожить объект, на который ссылается FRef. А я не знаю, существует ли еще этот объект, или он уже уничтожен. И у меня нет никаких способов узнать это.

Если в Method2 я просто напишу FRef.Free, то либо все пройдет нормально (объект еще существует), либо получим AV (объект уже был уничтожен и адрес, хранящийся в FRef стал недействительным). Результат совершенно непредсказуем - а самое печальное в том, что перед вызовом FRef.Free у меня нет возможности как-то проверить валидность ссылки FRef.

Принципиальный недостаток здесь в том, что нам дали способ сделать запрос на уничтожение объекта (вызов Free), но не дали способа узнать результат этого запроса. Вот в чем противоречие и в чем весь фокус.

В то же время, когда никакого подсчета ссылок нет и програмой я рулю сам (как в Delphi), то за валидностью ссылок я сам и слежу. И есть полная гарантия, что после вызова Free объект действительно уничтожен. Все просто, все однозначно и никаких проблем.


homm   (2007-11-06 11:54:00) [6]

> [80] Palladin ©   (06.11.07 11:43)

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

FS := TFileStream.Create('C:\file');
R := TReader.Create;
R.Stream := FS;
R.ReadValue(value);
FS.Free;
// здесь мы не знаем, уничтжен ли FS
R.Free;
// но здесь то точно знаем, что ему кранты.
DeleteFile('C:\file');


Согласен, нужно придерживатся каких-то еше правил, но помему это не слишком обременительно. С другой стороны, с точки зрения VCL этот пример вообще не коректен, т.к. я освободил FS до R?


vpbar   (2007-11-06 11:50:00) [7]

>>Palladin ©   (06.11.07 11:43) [80]
В системах со сборкой мусора (автоматическим потсчетом ссылок)  будет другая логика. Удаление объекта отдельно, а закрытие отдельно. И проблем не будет.
Если несколько ссылок на  TFileStream и вы освободили одну. Остальные продалжают ссылаться и для них TFileStream должен быть валидным. Поэтому закрывать handle нуждно после освобождения всех ссылок. Хотя с другой стороны если вы вызвав TFileStream.free ожидаете что закрытия хендла то такой подход приведет к ошибке. Вообщем фигня получается.
Это наверно тот случай когда моё предложение сохранять все ссылки в объекте имеет какю-то пользу.


Palladin   (2007-11-06 11:46:00) [8]

так же как и с любым другим объектом класса, при уничтожении объекта которого, бывают случаи когда нужно всегда быть уверенным, что никаких внешних связей с миром, которые он олицетворял, уже не существует, для обеспечения корректного продолжения работы программы


vpbar   (2007-11-06 11:44:00) [9]


> homm ©   (06.11.07 11:31) [78]
> Ты путаешь подсчет ссылок и автоматический подсчет ссылок

Нда. Просто я когда говорю "подсчет ссылок" имею ввиду автоматический. Но вообщем согласен.


Palladin   (2007-11-06 11:43:00) [10]


> homm ©   (06.11.07 11:26) [77]

Да тот же самый TFileStream, если я освобождаю объект, я точно знаю что handle закрыт и уж никак не ожидаю, что он закроется через пол часа...


vpbar   (2007-11-06 11:42:00) [11]

homm ©   (06.11.07 11:26) [77]
+5
homm ©   (06.11.07 11:31) [78]


homm   (2007-11-06 11:31:00) [12]

> [75] vpbar ©   (06.11.07 11:19)
> Вообще то имеет.

Ты путаешь подсчет ссылок и автоматическийподсчет ссылок. В механизме подсчета ссылок объект существует пока об обратном не сказано явно вызавом Free. После этого для вызвавшего участка кода(функции, компонента) объект не существует, существует ли он для какого-то другово участка кода не имеет значение.


homm   (2007-11-06 11:26:00) [13]

> [76] Юрий Зотов ©   (06.11.07 11:21)
> И Ваши что первый, что второй вариант на этот вопрос тоже
> не отвечают. Потому что ни FRef=nil, ни SC=TRUE не дают
> никаких гарантий. Эти условия лишь говорят о том, что метод
> Free был вызван, и все. А был ли объект при этом уничтожен,
> или нет (потому что где-то на него висят другие ссылки)
> - на ЭТОТ вопрос ответа нет.


Понял теперь о чем Вы.
А для чего это может понадобиться, точно знать что объект убит? Нужели Вы ожидаете некую полезнуюработу, происходящую в Destroy этого объекта? Помоему тогда логику приложения нужно менять.


Юрий Зотов   (2007-11-06 11:21:00) [14]

> homm ©   (06.11.07 11:09) [73]

В том-то и дело, что имеет. Если посчет ссылок работает, то ЛЮБОЙ вызов Free - это не уничтожение объекта, а всего лишь ЗАПРОС на его уничтожение. И этот запрос не дает никаких гарантий, что объект действительно будет уничтожен именно сейчас, а не через полчаса, другим запросом.

Поэтому при автоматическом уничтожении объекта по обнулению счетчика ссылок, если где-то хоть раз вызвано Free, то нет никаких способов узнать, существует ли еще объект, или он уже уничтожен. Это принципиально.

И Ваши что первый, что второй вариант на этот вопрос тоже не отвечают. Потому что ни FRef=nil, ни SC=TRUE не дают никаких гарантий. Эти условия лишь говорят о том, что метод Free был вызван, и все. А был ли объект при этом уничтожен, или нет (потому что где-то на него висят другие ссылки) - на ЭТОТ вопрос ответа нет.

И быть не может, понимаете?


vpbar   (2007-11-06 11:19:00) [15]

>>Юрий Зотов ©   (06.11.07 11:02) [70]
Конечно. Собственно хотел тоже предложить Ибо думаю, Вы согласитесь, что непреодолимых технических трудностей нет. Есть недостатки самой технологии.
>>Юрий, ну Вы же понимаете что этот пример никакого отношения не имеет к подсчету ссылок, решается точно так-же, как и сабжевая проблема.
Вообще то имеет. Если бы был действительно подсчет ссылок, то вопрос был бы лишен смыла. Ибо объект существует пока на него хоть кто-нибудь ссылается. Если мы можем обратиться к объекту через ссылку( она не равна nil) то он существует.
Т.е. вопрос можно ли обратиться к объект через какую нить ссылку решается проверкой не равна ли эта ссылка nil.
Если стоит вопрос сущеcтвеует ли объект вообще. То тут только глобальные флаги выставлять при его дестрое, по-моему.


homm   (2007-11-06 11:10:00) [16]

> [73] homm ©   (06.11.07 11:09)
> Вариант2:
> procedure TMyObject.Method1;
> if SomeCondition then begin
>   FRef.Free;
>   SC := TRUE;
> end;
> end;

Правильнее так:
procedure TMyObject.Method1;
 SC := SomeCondition;
 if SC then
   FRef.Free;
end


homm   (2007-11-06 11:09:00) [17]

> [72] Юрий Зотов ©   (06.11.07 11:05)

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

Вариант1:
procedure TMyObject.Method1;
if SomeCondition then
  FreeAndNil(FRef);
end;


Вариант2:
procedure TMyObject.Method1;
 if SomeCondition then begin
   FRef.Free;
   SC := TRUE;
 end;
end;


Юрий Зотов   (2007-11-06 11:05:00) [18]

> homm ©   (06.11.07 11:03) [71]

А в Method2 уже нет никаких SomeCondition.


homm   (2007-11-06 11:03:00) [19]

> ... // Вопрос на засыпку - как мне в этом месте кода узнать,
> ... // существует ли объект, на который ссылается поле  FRef,
> ... // или он уже уничтожен? Конечно, учитывая, что в других
> ... // местах программы тоже могут ссылки на этот же объект

if not SomeCondition then???


Юрий Зотов   (2007-11-06 11:02:00) [20]

> vpbar ©   (06.11.07 10:57) [68]

С Вашего разрешения, я эту дискуссию закончу, ладно?


Страницы:   1 2 3 4 5

Наверх

Титульная страница Поиск, карта сайта Написать письмо