3 апреля 2012 г.

Ответ на задачку №14

Ответ на задачку №14.

Я напомню код вопроса:
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. Все прочие посты, опубликованные первого апреля, шуток и ошибок не содержат*.

(*) Умышленных ошибок, я имею в виду.

4 комментария :

  1. "Ответ на задачку будет выложен, как обычно, через месяц."
    Месяц пролетел незаметно :)

    ОтветитьУдалить
  2. Александр, не поверишь, но в приведённом тобой примере может возникнуть ошибка.
    Я сам этого и не знал, и сначала тоже подумал, что шутка :с). Однако интереса ради открыл Graphics.pas и проследил (по коду) цепочки событий, которые кроются за простыми действиями в примере.
    Оказывается, там есть переменная BitmapCanvasList - туда сохраняются контексты (грубо - Canvas.Handle), и есть процедура FreeMemoryContexts, которая освобождает все не заблокированные контексты, и которая (внимание!) вызывается главным (VCL) потоком приложения.
    Т.е. если приведённый пример (без блокировок Canvas) запускать отдельным потоком, и в промежутках между внутренними вызовами внутри Test.Canvas.TextExtent отработает код из главного VCL-потока - значение HFont внутри текущего DC сбросится и функция вернёт некорректный результат.

    И даже нашёл на некоторых форумах (по слову FreeMemoryContexts), что люди с похожей проблемой сталкивались.
    А вот утечки там вроде бы и нет, а, возможно, и может возникнуть. Чтобы это выявить, надо полностью расписать последовательность вызовов кода одного потока (примера) и кода VCL (в два столбца) и просмотреть это всё - на это у меня терпения не хватило :с)

    Кстати, только после такого беглого анализа этого исходника я проникся реализацией Canvas и некоторых других вещей, до этого (лет 5 назад) мне как-то было не по себе от обилия кода и непонятных Handle, ResData и прочих сущностей.. поэтому спасибо за задачку, мне было интересно!

    ОтветитьУдалить
  3. Задачки задачками...) А уже не терпится увидеть реализацию UI в разрабатываемой системе плагинов...

    ОтветитьУдалить
  4. Ошибка в злоупотреблении FreeAndNil :)

    ОтветитьУдалить

Можно использовать некоторые HTML-теги, например:

<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>

Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и (опционально) ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку (поддерживается OpenID).

Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.

Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.