SCRIBE
Гость
|
|
« : 06-05-2016 07:48 » |
|
Привет, друзья. Почитал я гугл, чет никак. Проблема только при обработке сообщения ОС. Есть такое сообщение как WM_WINDOWPOSCHANGED, срабатывает при изменении размеров окна. Хочу собственно сохранить эти значения для удобства пользователя. Включим отображение утечек: ReportMemoryLeaksOnShutdown := true; Обработка сообщения: procedure WMWindowPosChanged(var aMessage: TWMWindowPosChanged); message WM_WINDOWPOSCHANGED;
procedure TfmMain.WMWindowPosChanged(var aMessage: TWMWindowPosChanged); begin inherited; SaveFormSize(fmMain.Width, fmMain.Height); // метод формы. на котором собственно и возникает утечка end;
procedure TfmMain.SaveFormSize(const aWidth, aHeight: integer); begin if Assigned(AppOptions) then begin AppOptions.SetOption('GUI', 'FormWidth', inttostr(aWidth)); // Передаются в метод как const AppOptions.SetOption('GUI', 'FormHeight', inttostr(aHeight)); // Эти самые строки, что не освобождаются, 4шт. end; end;
TOption = packed record Section: string; Name: string; Value: string;
TOptions = class private FOptions: array of TOption;
function TOptions.SetOption(const aSection, aName, aValue: string): boolean; var i: integer; Founded: boolean; begin Result:= False; Founded:= false; if (aSection = '') or (aName = '') or (aValue = '') then raise Exception.Create('Some of option agrument is empty!'); for i := 0 to high(FOptions) do begin if (FOptions[i].Section = aSection) and (FOptions[i].Name = aName) then begin FOptions[i].Value:= aValue; Founded:= true; end; end; if not Founded then begin SetLength(FOptions, length(FOptions) + 1); FOptions[high(FOptions)].Section:= aSection; FOptions[high(FOptions)].Name:= aName; FOptions[high(FOptions)].Value:= aValue; end; FLastChange:= GetTime; FEmpty:= false; Result:= true; end; Что интересно, эти самые методы вызываются при закрытии программы, и утечек нет, а вот в обработчике есть. Не пойму в чем соль.
|
|
|
Записан
|
|
|
|
zubr
Гость
|
|
« Ответ #1 : 06-05-2016 08:24 » |
|
SetLength(FOptions... память выделяется, а где она потом удаляется?
|
|
|
Записан
|
|
|
|
SCRIBE
Гость
|
|
« Ответ #2 : 06-05-2016 08:25 » |
|
destructor TOptions.Destroy; begin SetLength(FOptions, 0); inherited Destroy; end;
|
|
|
Записан
|
|
|
|
SCRIBE
Гость
|
|
« Ответ #3 : 06-05-2016 08:37 » |
|
Даже так пробовал. TOption = packed record Section: string; Name: string; Value: string; Procedure Clear; end;
procedure TOption.Clear; begin Self:= Default(TOption); end;
destructor TOptions.Destroy; var i: integer; begin for i := 0 to high(FOptions) do FOptions[i].Clear; SetLength(FOptions, 0); inherited Destroy; end;
|
|
|
Записан
|
|
|
|
x77
Модератор
Offline
Пол:
меняю стакан шмали на обратный билет с Марса.
|
|
« Ответ #4 : 22-06-2016 00:08 » |
|
в дельфях работа с памятью люто централизована. всеми выделениями и освобождениями памяти занимается только менеджер памяти. с 2007, если не ошибаюсь, его роль выполняет FastMM. и факт утечки константируется на основе того простого факта, что кол-во выделений памяти неравно кол-ву освобождений памяти. больше ReportMemoryLeakOnShutdown ничего не даст. и если оно выдает ошибку на методе, это означает только, что внутри метода инициализировано больше переменных, чем освобождено. и это логично, если вы хотите внутри метода созранить какую-то инфу в каком-то объекте, так, чтобы он был доступен вне этого метода, зачем вы создаете этот объект внутри метода? создайте и инициализируйте его, например, в FormCreate, освобождайте в FormDestroy, в внутри метода просто заполняйте нужными значениями уже выделенные в памяти ячейки массива. и FastMM будет спокоен, аки испанская балерина под галаперидолом. З.Ы. в сабж: http://www.gunsmoker.ru/2009/05/blog-post_24.html
|
|
« Последнее редактирование: 22-06-2016 00:09 от x77 »
|
Записан
|
|
|
|
SCRIBE
Гость
|
|
« Ответ #5 : 22-06-2016 08:56 » |
|
Прочитал и все равно не понимаю в чем проблема. Да, я создал объект (между прочим было без разницы, packed record там или class) в методе, что тут такого, я его и освободил при выходе из программы деструктором, как порядочный гражданин прошелся по массиву. В других модулях также используются динамические массивы с объектами, и никаких проблем.
В любом случае, пришлось просто отказаться вызывать его в обработчике события WM_WINDOWPOSCHANGED, а по старинке при закрытии программы. Утечка пропала.
И спасибо за ссыль.
|
|
|
Записан
|
|
|
|
x77
Модератор
Offline
Пол:
меняю стакан шмали на обратный билет с Марса.
|
|
« Ответ #6 : 22-06-2016 16:47 » |
|
насколько я понимаю, проблема в том, что метод вызывался дважды. соответственно, и объект создавался дважды, а освобождался - только один раз, в деструкторе. насчет packed record. по дефолту записи выравниваются по границе в 8 байт. что увеличивает скорость доступа к ним. размер string в зависимости от версии дельфи может "плавать" от 256 байт до нескольких гигабайт, поэтому попытки "паковать" структуры с этим типом - они какбе сомнительны при использовании packed record строки лучше жестко типизовать оббявляя заранее их рамер, string [10], например, только тогда это будет иметь какой-то смысл.
|
|
« Последнее редактирование: 22-06-2016 17:05 от x77 »
|
Записан
|
|
|
|
SCRIBE
Гость
|
|
« Ответ #7 : 29-06-2016 19:30 » |
|
метод вызывается кучу раз, когда окно меняет размер или перемещается, и как он может создастся дважды, будет просто новый элемент массива, который потом освобождается. и оно там проверяет, нет ли в массиве объектов с таким именем параметра, если есть, меняет в нем значение, иначе новый элемент массива, все как бы)
|
|
|
Записан
|
|
|
|
x77
Модератор
Offline
Пол:
меняю стакан шмали на обратный билет с Марса.
|
|
« Ответ #8 : 02-07-2016 22:43 » |
|
ну судя по коду (я так понял, тут не весь код), память выделяется многократно (по кол-ву вызовов метода), а освобождается только разово. утилиты контроля памяти не делают ничего сверхЪестественного, они просто переопределяют системные методы резервирования и освобождения памяти. вставляя туда свои собственные счетчики выделений и освобождений. дельфийский менеджер памяти - это не черный ящик, вы запросто можете написать свой и назначить его вместо. и когда память выделялась больше раз, чем освобождалась - они рапортуют о memory leak, вот и все. попробуйте задавать массив при инициализации приложения, SetLength - это тоже выделение памяти. не видя всего кода - это гадание на кофейной гуще, поэтому извините, конечно, может я просто что-то не догоняю
|
|
|
Записан
|
|
|
|
SCRIBE
Гость
|
|
« Ответ #9 : 19-07-2016 06:25 » |
|
|
|
|
Записан
|
|
|
|
x77
Модератор
Offline
Пол:
меняю стакан шмали на обратный билет с Марса.
|
|
« Ответ #10 : 01-08-2016 21:05 » |
|
сорри, я тут набегами, и нотификация почему-то была отключена. если ты не против, я поковыряю код, не обещаю правда, что быстро )
|
|
|
Записан
|
|
|
|
v2
Помогающий
Offline
|
|
« Ответ #11 : 16-09-2016 16:07 » |
|
Application.Run; end. и вроде энд, но тут прилетает хеппи WM_WINDOWPOSCHANGED )) procedure TfmMain.FormClose(Sender: TObject; var Action: TCloseAction); begin Tag := -1; // можно так скостылить FreeApp(Action); end; //+ procedure TfmMain.WMWindowPosChanged(var aMessage: TWMWindowPosChanged); begin if Tag=0 then begin // ... SaveFormSize(fmMain.Width, fmMain.Height); inherited; end; end; я не поклонник FreeAndNil, но если б он там был ... )) procedure TfmMain.FreeApp(var Action: TCloseAction); ... if Assigned(AppOptions) then begin AppOptions.SetOption('GUI', 'GroupList', IntToStr(tvMain.Width)); AppOptions.SetOption('GUI', 'FormLeft', inttostr(fmMain.Left)); AppOptions.SetOption('GUI', 'FormTop', inttostr(fmMain.Top)); SaveFormSize(fmMain.Width, fmMain.Height); if not AppOptions.SaveUserOptions() then raise Exception.Create('Cant''t save user options!'); // после нас хоть потоп - а, ну да, на то оно и 'raise' ))) AppOptions.Free; // может FreeAndNil ? end; ...
procedure TfmMain.SaveFormSize(const aWidth, aHeight: integer); begin if Assigned(AppOptions) then // да, FreeAndNil !!! begin AppOptions.SetOption('GUI', 'FormWidth', inttostr(aWidth)); AppOptions.SetOption('GUI', 'FormHeight', inttostr(aHeight)); end; end; и еще (что заметилось): function TOptions.SetOption(const aSection, aName, aValue: string): boolean; ... for i := 0 to high(FOptions) do begin if (FOptions[i].Section = aSection) and (FOptions[i].Name = aName) then begin FOptions[i].Value:= aValue; Founded:= true; Break; // не помешает end; end; ...
|
|
|
Записан
|
|
|
|
SCRIBE
Гость
|
|
« Ответ #12 : 17-09-2016 17:41 » |
|
Насчет Assigned, да, обманчив он, чисто проверка на Nil, работать должен с FreeAndNil.
Вот значит как, объект с настройками освободился, а виндовс пытается в него запихнуть значения... Спасибо огромное человек, флаг на начало закрытия формы уже можно сообразить.
|
|
|
Записан
|
|
|
|
|