Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Начинающим";
Текущий архив: 2007.12.02;
Скачать: [xml.tar.bz2];

Вниз

Проблема с TFileStream   Найти похожие ветки 

 
ProgRAMmer Dimonych ©   (2007-11-05 22:45) [0]

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

Дело в том, что 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-05 22:58) [1]


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

Ну в чем проблема? То что отладчик делфи показывает поля объекта после его освобождения? Так ведь память после освобождения объекта не сразу то затирается.


 
homm ©   (2007-11-05 23:02) [2]

> [0] ProgRAMmer Dimonych ©   (05.11.07 22:45)
> Если я правильно понимаю немецкий, то вместо метода Free
> следует юзать процедуру FreeAndNil().

А при чем здесь TFileStream?


 
ProgRAMmer Dimonych ©   (2007-11-05 23:12) [3]

> vpbar ©   (05.11.07 22:58) [1]
> Ну в чем проблема? То что отладчик делфи показывает поля
> объекта после его освобождения? Так ведь память после освобождения
> объекта не сразу то затирается.

Да вот ведь вещь: не всегда так, иногда nil. Но это, наверное, не основное. Хотя какое не сразу? Если объект удалён, то вместо Объект=(Свойство:Значение; Свойство:Значение) там должно по идее отображаться Объект=nil.

> homm ©   (05.11.07 23:02) [2]
> > [0] ProgRAMmer Dimonych ©   (05.11.07 22:45)
> А при чем здесь TFileStream?

По приведённой мной ссылке - ветка форума, где, насколько я могу судить, пытаясь вникнуть в суть написанного на незнакомом мне языке, обсуждается проблема использования функции Assigned() по отношению к экземпляру TFileStream. Вроде как вечно True возвращает, а это уже грозит AV"ами и прочими радостями. Если сформулировать мой вопрос "в лоб", то: как правильно удалять TFileStream?


 
Anatoly Podgoretsky ©   (2007-11-05 23:16) [4]


> Если объект удалён, то вместо Объект=(Свойство:Значение;
>  Свойство:Значение) там должно по идее отображаться Объект=nil.
>

А все ветки об этом говорят, что это ересь и блажь.


 
Юрий Зотов ©   (2007-11-05 23:17) [5]

> что, на мой взгляд, имеет печальные последствия

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

Ну и что? Какие же тут печальные последствия?


 
Юрий Зотов ©   (2007-11-05 23:24) [6]

> ProgRAMmer Dimonych ©   (05.11.07 23:12) [3]

> как правильно удалять TFileStream?

Точно так же, как и ЛЮБОЙ другой объект - вызовом Free.

Если при удалении объекта нужно еще и обнулить какую-то переменную, то можно вызвать FreeAndNil. Она выполнит 2 операции - и объект уничтожит (тем же вызовом Free), и переменную обнулит. Причем это ДВЕ совершенно РАЗНЫЕ операции. Их можно выполнить и самому, ручками.


 
ProgRAMmer Dimonych ©   (2007-11-05 23:27) [7]

ОК, даю кусок кода...

function TMyObject.SaveToFile(FileName:ANSIString):Boolean;
var
Tmp:TFileStream;
FN:ANSIString;
i:Integer;
N:Word;
begin
Result:=False;
FN:=TempDir+ExtractFileName(FileName)+"~";
DeleteFile(PANSIChar(FN));
try Tmp:=TFileStream.Create(FN,fmCreate); except Exit; end;
{Пишем в файл}
if Assigned(Tmp) then Tmp.Free;
===>>> if Assigned(FFile) then FFile.Free; {Здесь вызывается Free}
if FFileName=FileName then DeleteFile(PANSIChar(FFileName));
RenameFile(FN,FileName);
Self.LoadFromFile(FileName);
Result:=True;
end;

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


Что скажете?


 
vpbar ©   (2007-11-05 23:28) [8]

жуть


 
ProgRAMmer Dimonych ©   (2007-11-05 23:29) [9]

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

Вы меня опередили. Но по этому куску кода без FreeAndNil - никуда. Так ведь? Ну, если не считать саморучно написанной замены.


 
ProgRAMmer Dimonych ©   (2007-11-05 23:30) [10]

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


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


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

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


 
ProgRAMmer Dimonych ©   (2007-11-05 23:33) [12]

> 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:34) [13]

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

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

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


 
Anatoly Podgoretsky ©   (2007-11-05 23:35) [14]

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

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

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


 
vpbar ©   (2007-11-05 23:37) [15]

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

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


 
Anatoly Podgoretsky ©   (2007-11-05 23:39) [16]


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

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


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

> 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 с файловой системой.


 
ProgRAMmer Dimonych ©   (2007-11-05 23:44) [18]

> 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, было бы чтение.


 
ProgRAMmer Dimonych ©   (2007-11-05 23:47) [19]

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

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


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

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

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


 
korneley ©   (2007-11-05 23:48) [21]

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


 
vpbar ©   (2007-11-05 23:51) [22]


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

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


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

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


 
ProgRAMmer Dimonych ©   (2007-11-05 23:51) [24]

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

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

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


 
vpbar ©   (2007-11-05 23:52) [25]

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


 
korneley ©   (2007-11-05 23:58) [26]


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


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

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

Да ну


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

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

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

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

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

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

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

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

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

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

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


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


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

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

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


 
Юрий Зотов ©   (2007-11-06 00:07) [30]

vpbar ©   (05.11.07 23:52) [25]

> Ничего сложного для разработчиков.

В самом деле? Тогда один простенький вопросик можно? А каким-таким волшебным образом в методе Free объекта можно узнать о том, что такая переменная вообще существует?

А ведь их может быть и не одна, запросто.


 
korneley ©   (2007-11-06 00:10) [31]


> С объектом связть список. В который добавлять указатель
> на переменную при присваивании объекта.
"Догнать Савранского - это утопия..." (с) :)


 
Германн ©   (2007-11-06 00:13) [32]


> Юрий Зотов ©   (06.11.07 00:03) [28]

А с телефонной книжкой разъяснение было интереснее :-)


 
vpbar ©   (2007-11-06 00:13) [33]


> Юрий Зотов ©   (06.11.07 00:07) [30]
> А каким-таким волшебным образом в методе Free объекта можно
> узнать о том, что такая переменная вообще существует?
Передавать в free указатель на переменную.

>>А ведь их может быть и не одна, запросто.
Вот это некоторая проблема.
Как вариант. Добавить в объект указатель на список ссылок на переменные (далее ССП).
var a,b,c:TObject;
....
a:=TObject.Create; // в том числе добавляем ссылку на a в ССП
b:=a; // добавляем ссылку на b в ССП
....
c:=b; // добавляем ссылку на c в ССП
b:=nil ; // удаляем ссылку на b из ССП
c.free; // освобождаем и обнуляем все ссылки из ССП
//
Про многопоточность думать еще надо. Хотя тоже решаемо.


 
ProgRAMmer Dimonych ©   (2007-11-06 00:14) [34]

> Юрий Зотов ©   (06.11.07 00:03) [28]

Не беспокойтесь Вы так, общее представление у меня есть. Скорее всего, с толку сбило то, что обычно не пользуюсь вот такими межпроцедурными вещами типа FFile в этом случае. Т.е. обычно Tmp:=TStringList.Create; в начале процедуры и Tmp.Free; в конце - и всё классно. А тут чего-то просто не подумал.

> vpbar ©   (06.11.07 00:03) [29]
> Ну тогда ссылки считать, и не освобождать пока все ссылки
> не обнулятся.

В COM, кажется, так делается?

===

А вообще - всем спасибо, по моему вопросу - всё.


 
vpbar ©   (2007-11-06 00:16) [35]

>>ProgRAMmer Dimonych ©   (06.11.07 00:14) [34]
>В COM, кажется, так делается?
Ага. В делфи, тоже, но только для интерфейсов.


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

> vpbar ©   (06.11.07 00:13) [33]

a:=TObject.Create; // в том числе добавляем ссылку на a в ССП

КАК?

Каким образом объект в своем конструкторе может узнать о том, что где-то там вообще существует какая-то переменная a?

Тем более, узнать ее адрес.


 
Юрий Зотов ©   (2007-11-06 00:25) [37]

> > vpbar ©   (06.11.07 00:13) [33]
b:=a; // добавляем ссылку на b в ССП
А это и тем более фантастика.


 
ProgRAMmer Dimonych ©   (2007-11-06 00:27) [38]

> Юрий Зотов ©   (06.11.07 00:24) [36]
> Каким образом объект в своем конструкторе может узнать о
> том, что где-то там вообще существует какая-то переменная
> a?
>
> Тем более, узнать ее адрес.

> Юрий Зотов ©   (06.11.07 00:25) [37]
> > > vpbar ©   (06.11.07 00:13) [33]
> b:=a; // добавляем ссылку на b в ССП
> А это и тем более фантастика.

Компилятор нагрузить этим делом. Ну, в смысле этот, как его? Который про адреса переменных всё знает? Линковщик. :)


 
vpbar ©   (2007-11-06 00:28) [39]

>>Юрий Зотов ©   (06.11.07 00:24) [36]
Гм. А как объект знает какого он типа? И какую виртуальную функцию надо вызывать?
Компилятор добавит нужную информацию.


 
vpbar ©   (2007-11-06 00:31) [40]

Юрий, я уже начинаю сомневатся в своей правоте :) Хотя и не привык слепо верить авторитетам. Просто не пойму, что тут фантастичного.
ЗЫ. Видимо пора спать, чтобы червь сомнений не мутил мой светлый разум. :)



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

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

Наверх





Память: 0.57 MB
Время: 0.062 c
2-1194517429
{ент
2007-11-08 13:23
2007.12.02
Карты


2-1194502707
Slim
2007-11-08 09:18
2007.12.02
Функции работы со строковыми типами в Delphi


1-1189590777
em240
2007-09-12 13:52
2007.12.02
Завершение потока


15-1193945573
Ricks
2007-11-01 22:32
2007.12.02
В шоке от украинских новостей...


15-1193727308
Garik888
2007-10-30 09:55
2007.12.02
Компонент для печати





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