![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Проблема с TFileStream
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]
С Вашего разрешения, я эту дискуссию закончу, ладно?