Я напомню код вопроса:
function CalcTextSize(const AFont: TFont; const AText: String): TSize; var Test: TBitmap; begin Test := TBitmap.Create; try Test.Canvas.Font := AFont; Result := Test.Canvas.TextExtent(AText); finally FreeAndNil(Test); end; end;И проблема здесь заключается в...
Ни в чём она не заключается, нет тут проблемы :) Ни утечки памяти, ни какой-либо иной*. Это была шутка.
Задачка сделана копией/по мотивам известной ошибки в первой задачке сайта Delphi Puzzles. Идея была в том, что вы перезаписываете переменную
Font
(в оригинале - Lines
). Перезапись переменной означает потерю ссылки на предыдущее значение (объект), что ведёт к неминуемой утечке ресурсов. Ошибка в рассуждениях заключается в том, что у нас не переменная (поле), а свойство. И доступ к свойству происходит через метод-акцессор, который достаточно умён, чтобы не затирать поле, а использовать метод Assign
для копирования содержимого объекта. Таким образом, никакой проблемы нет, свойства одного объекта будут скопированы в другой объект. Потери указателя не будет.P.S. Все прочие посты, опубликованные первого апреля, шуток и ошибок не содержат*.
(*) Умышленных ошибок, я имею в виду.
"Ответ на задачку будет выложен, как обычно, через месяц."
ОтветитьУдалитьМесяц пролетел незаметно :)
Александр, не поверишь, но в приведённом тобой примере может возникнуть ошибка.
ОтветитьУдалитьЯ сам этого и не знал, и сначала тоже подумал, что шутка :с). Однако интереса ради открыл Graphics.pas и проследил (по коду) цепочки событий, которые кроются за простыми действиями в примере.
Оказывается, там есть переменная BitmapCanvasList - туда сохраняются контексты (грубо - Canvas.Handle), и есть процедура FreeMemoryContexts, которая освобождает все не заблокированные контексты, и которая (внимание!) вызывается главным (VCL) потоком приложения.
Т.е. если приведённый пример (без блокировок Canvas) запускать отдельным потоком, и в промежутках между внутренними вызовами внутри Test.Canvas.TextExtent отработает код из главного VCL-потока - значение HFont внутри текущего DC сбросится и функция вернёт некорректный результат.
И даже нашёл на некоторых форумах (по слову FreeMemoryContexts), что люди с похожей проблемой сталкивались.
А вот утечки там вроде бы и нет, а, возможно, и может возникнуть. Чтобы это выявить, надо полностью расписать последовательность вызовов кода одного потока (примера) и кода VCL (в два столбца) и просмотреть это всё - на это у меня терпения не хватило :с)
Кстати, только после такого беглого анализа этого исходника я проникся реализацией Canvas и некоторых других вещей, до этого (лет 5 назад) мне как-то было не по себе от обилия кода и непонятных Handle, ResData и прочих сущностей.. поэтому спасибо за задачку, мне было интересно!
Задачки задачками...) А уже не терпится увидеть реализацию UI в разрабатываемой системе плагинов...
ОтветитьУдалитьОшибка в злоупотреблении FreeAndNil :)
ОтветитьУдалить