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().

Это так?


vpbar   (2007-11-06 00:03:00) [61]


> korneley ©   (05.11.07 23:58) [26]

Ну тогда ссылки считать, и не освобождать пока все ссылки не обнулятся.

> А если ссылок много? (на то они и ссылки) Как прикажете
> их искать с целью дальнейшего за(об)ниления?
Хотя можно и искать. С объектом связть список. В который добавлять указатель на переменную при присваивании объекта. Тока тут тоже проблемы могут всякие.


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

Димоныч, вот смотри.

Объект - это, грубо говоря, кусок динамической памяти. В твоей программе может быть хоть сто указателей на этот кусок. Хоть миллион. А может быть и один. Дело не в этом, а в том, что сам объект об этих указателях ничего не знает и знать не может.

ОК, вот теперь ты этот объект грохнул. Как он может при своем грохании обнулить все ссылки на себя, ежели он об этих ссылках ничего не знает?

Не может он их очистить. Он и не чистит. И ты сам - тоже не чистишь. Значит - что? значит, все эти указатели какими были, такими и остались, раз их никто не менял.

А куда теперь они стали указывать? А туда же, куда и раньше указывали - но теперь попытка обратиться по этому адрусу даст AV, потому что память-то по нему уже освобождена!!! Попытка обращения к нераспределенной области динамической памяти.

Теперь - что делают Free, FreeAndNil и Assigned?

Free проверяет, равен ли Self nil'у. Если нет - вызывает деструктор, если нет - не делает ничего. Это просто безопасный вызов деструктора.

FreeAndNil выполняет 2 независимыеоперации: вызывает Free и обнуляет переданный ей указатель на объект. То же самое ты можешь сделать и сам.

Assigned - просто проверяет, равна ли переданная ей ссылка nil'у. Больше ничего.

Теперь въехал?
:о)


Anatoly Podgoretsky   (2007-11-06 00:02:00) [63]

> vpbar  (05.11.2007 23:52:25)  [25]

Да ну


korneley   (2007-11-05 23:58:00) [64]


> ... сделать так чтобы метод Free обнилял переменную...
А если ссылок много? (на то они и ссылки) Как прикажете их искать с целью дальнейшего за(об)ниления?


vpbar   (2007-11-05 23:52:00) [65]

>>korneley ©   (05.11.07 23:48) [21]
Нда. Намек разработчикая языка сделать так чтобы метод Free обнилял переменную. Ничего сложного для разработчиков.


korneley   (2007-11-05 23:51:00) [66]

Упс... "нил" с одним английским "L" пишется... В отличие от "NULL" :)


vpbar   (2007-11-05 23:51:00) [67]


> FFile.Free не должно вызывать ошибку, т.к. Free - это, если
> верить справке, безопасная замена для Destroy. Но фишка
> именно в том, что Assigned(FFile) даёт True даже после вызова
> метода FFile.Free. Т.е., похоже, опять приходим к FreeAndNil.
>  А вот как это отразится на использовании памяти, не будет
> ли утечек при таком подходе?

Вы не понимаете что происходит при вызове FFile.Free.
При этом вызывается деструктор освобождается память на которую указывет FFile. А вот само значение FFile не меняется. Т.е. оно указывает на уже освобожденную память. Assigned(FFile) - естественно ведь значение FFile не меняли и оно не равно nil. Приходим к FreeAndNil. Т.е. если возможно после освобождения обращение а этой переменной нужно обнилять ее. Что и делает FreeAndNil. И никаких утечек FreeAndNil не может внести


ProgRAMmer Dimonych   (2007-11-05 23:51:00) [68]

> korneley ©   (05.11.07 23:48) [21]
> За последние недели, раз в третий (или четвертый?) читаю:
>  "я экземляр объекта освободил/открепил, а ссылка на него
> чего-то не нил..." А ты ея занилил? Ссылку-то? Хоть FreeAndNill,
>  хоть := nill;... Просто не грабли, а ГРАБЛИ какие-то...
>  Как у Хармса: "...а старушки всё падали..."

Ну, бывает, чего уж там. :)

P.S. Смайлик для виноватой улыбки вспомнить не получилось.


korneley   (2007-11-05 23:48:00) [69]

За последние недели, раз в третий (или четвертый?) читаю: "я экземляр объекта освободил/открепил, а ссылка на него чего-то не нил..." А ты ея занилил? Ссылку-то? Хоть FreeAndNill, хоть := nill;... Просто не грабли, а ГРАБЛИ какие-то... Как у Хармса: "...а старушки всё падали..."


Юрий Зотов   (2007-11-05 23:47:00) [70]

> Но фишка именно в том, что Assigned(FFile) даёт True даже после вызова
> метода FFile.Free.

Димоныч, вот скажи мне простым человеческим языком - а почему после вызова FFile.Free вызов Assigned(FFile) вдруг должен дать False? Вот с какой такой радости?


ProgRAMmer Dimonych   (2007-11-05 23:47:00) [71]

> Юрий Зотов ©   (05.11.07 23:42) [17]

Кажется, понял. Тогда получается, что, действительно, без присвоения указателю FFile:=nil будет глючить. Так вроде?


ProgRAMmer Dimonych   (2007-11-05 23:44:00) [72]

> Anatoly Podgoretsky ©   (05.11.07 23:34) [13]
> Это глупый код, правильный просто FFile.Free

Согласен, автоматом написал, не вникнув.

> Anatoly Podgoretsky ©   (05.11.07 23:35) [14]
> А нафига здесь функция, которая всегда возвращает False

Это урезанный вариант функции, результат зависит от того, как пройдёт чтение из файла.

> vpbar ©   (05.11.07 23:37) [15]
> Просто непонятно кто с FFile еще работает.

FFile в начале функции проассоциирован с текущим файлом. Между двумя указанными мной строками возникает проблема. Все действия над FFile между этими строками я оставил, убрал только то, что не относится к нему. Объект не пользуется никаких там thread'ов и прочей радости.

> А вообще не обращайтесь к объектам после удаления. Для надежность
> обниляйте их.

FFile.Free не должно вызывать ошибку, т.к. Free - это, если верить справке, безопасная замена для Destroy. Но фишка именно в том, что Assigned(FFile) даёт True даже после вызова метода FFile.Free. Т.е., похоже, опять приходим к FreeAndNil. А вот как это отразится на использовании памяти, не будет ли утечек при таком подходе?

> {Чтение файла, которого нет из-за AV} - это как. еслибы
> не было ав вайл был бы?

Это значит, если бы не было AV, было бы чтение.


Юрий Зотов   (2007-11-05 23:42:00) [73]

> ProgRAMmer Dimonych ©   (05.11.07 23:27) [7]

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

Долее вызывается LoadFromFile(FileName), в котором есть строчка:
if Assigned(FFile) then FFile.Free

Поскольку значение самой FFile не изменилось, Assigned дает true и делается попытка вызвать метод объекта Free через ссылку на уже уничтоженный экземпляр объекта. Здравствуй, Access Violation.

И никакие наличия-отсутствия файла здесь абсолютно ни при чем. Никакие файлы здесь вообще ни при чем.

> Что скажете?

1. Код отвратительный.
2. Непонимание указателей.
3. Непонимание работы объекта TFileStream с файловой системой.


Anatoly Podgoretsky   (2007-11-05 23:39:00) [74]


> Не в том смысле "Кусок", наверно Анатолий имел ввиду.

В смысле, что про него больше нечего сказать.


vpbar   (2007-11-05 23:37:00) [75]

Не в том смысле "Кусок", наверно Анатолий имел ввиду.
Просто непонятно кто с FFile еще работает. А вообще не обращайтесь к объектам после удаления. Для надежность обниляйте их.

{Чтение файла, которого нет из-за AV} - это как. еслибы не было ав вайл был бы?


Anatoly Podgoretsky   (2007-11-05 23:35:00) [76]

> ProgRAMmer Dimonych  (05.11.2007 23:33:12)  [12]

function TMyObject.LoadFromFile(FileName:ANSIString):Boolean;
begin
Result:=False;

А нафига здесь функция, которая всегда возвращает False


Anatoly Podgoretsky   (2007-11-05 23:34:00) [77]

> ProgRAMmer Dimonych  (05.11.2007 23:33:12)  [12]

===>>> if Assigned(FFile) then FFile.Free; {Здесь вызывается Free}

Это глупый код, правильный просто FFile.Free


ProgRAMmer Dimonych   (2007-11-05 23:33:00) [78]

> Anatoly Podgoretsky ©   (05.11.07 23:30) [11]
> > Что скажете?
> Это действительно Кусок.

Сокращаю, оставляю только работу с TFileStream'ом.

function TMyObject.SaveToFile(FileName:ANSIString):Boolean;
begin
Result:=False;
===>>> if Assigned(FFile) then FFile.Free; {Здесь вызывается Free}
Self.LoadFromFile(FileName);
Result:=True;
end;

function TMyObject.LoadFromFile(FileName:ANSIString):Boolean;
begin
Result:=False;
===>>> if Assigned(FFile) then FFile.Free; {А здесь у нас AV}
{Чтение файла, которого нет из-за AV}
end;


Anatoly Podgoretsky   (2007-11-05 23:30:00) [79]


> Что скажете?

Это действительно Кусок.


ProgRAMmer Dimonych   (2007-11-05 23:30:00) [80]

P.S. К фрагменту кода: FFile объявлена в секции private TMyObject и предназначена для того, чтобы удерживать используемый файл.


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

Наверх

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