вторник, 21 января 2014 г.

Ссылка. Документация от Embarcadero о совместимости "procedure" и "reference to procedure"

http://docwiki.embarcadero.com/RADStudio/XE5/en/Anonymous_Methods_in_Delphihttp://docwiki.embarcadero.com/RADStudio/XE5/en/Anonymous_Methods_in_Delphi

Для таких как Я "не читавших".

Там ЕСТЬ О ЧЁМ подумать.

Например о том - "как ресурсы захватываются" и "какое их время жизни".

И "как это связано с ARC".

Для "пищи ума" приведу простейший пример:

program Test;

type
 X = reference to procedure;
 A = class
  protected
   f_Field : Integer;
  public
   procedure B;
 end; // A

var
 l_X : X;

procedure A.B;
begin
 l_X := procedure (Self.f_Field := Self.f_Field + 1;);
end;

var
 l_A : A;
begin
 l_A := A.Create;
 l_A.B;
 l_A.Free;
 l_X;
end;

-- каково поведение этого кода?

P.S. Ну и ещё "пища для мозгов" - https://plus.google.com/+RomanYankovsky/posts/cvJgAAZmhw6

10 комментариев:

  1. Ужасное поведение у этого кода. Во первых, он не скомпилируется.
    А во-вторых, при отключенном флаге контроля за использованием освобождённой памяти в FastMM он скорее всего не выдаст никакой ошибки.
    Delphi вообще в некоторых случаях довольно бесстрастно проглатывает попытки использования освобождённых объектов.

    ОтветитьУдалить
    Ответы
    1. "Во первых, он не скомпилируется."

      Почему это? Я ПРИЗНАЮСЬ - его не компилировал. Но он ПО-МОЕМУ не противоречит ДОКУМЕНТАЦИИ. Или ПРОТИВОРЕЧИТ? (Какому пункту?)

      "А во-вторых, при отключенном флаге контроля за использованием освобождённой памяти в FastMM он скорее всего не выдаст никакой ошибки."

      Вот это - НЕ ПОНЯЛ! Можно подробнее?

      Удалить
    2. Не скомпилируется. Забыл begin-end в анонимной процедуре и в конце должен быть end с точкой. Но это мелочи. Главное, сам код понятен.

      Удалить
  2. как Роман верно заметил там не хватает begin и end.

    > Вот это - НЕ ПОНЯЛ! Можно подробнее?
    У меня этот код выполнился без видимых ошибок.

    > А во-вторых, при отключенном флаге контроля за использованием освобождённой памяти в FastMM он скорее всего не выдаст никакой ошибки
    В FastMM (полной версии) есть директива, при включении которой FastMM будет следить, чтобы никто не использовал уже освобождённую память. И в случае чего, выдавать exception.

    ОтветитьУдалить
    Ответы
    1. "как Роман верно заметил там не хватает begin и end."
      -- ну блин.. вы же понимаете, что "глупо к запятым цепляться". Я же "мысль" озвучил.

      Как Роман написал:
      "Но это мелочи. Главное, сам код понятен."

      :-)

      "У меня этот код выполнился без видимых ошибок."
      -- ГОТОВ ПОВЕРИТЬ! :-) Но это у МЕНЯ ТОЛЬКО есть ощущение, что то что КОД ВЫПОЛНЯЕТСЯ - это БОЛЕЕ ЧЕМ СТРАННО?

      Или я что-то пропустил в этой жизни?

      Ну БЕЗ ARC конечно.

      С ARC - ВСЁ МОГУ ПОНЯТЬ, БЕЗ ARC - не понимаю.

      Ну и в свете этого поста - "у меня глаза ОТКРЫЛИСЬ" - почему Emb - так ARC ПРОДВИГАЕТ.

      Удалить
    2. "как Роман верно заметил там не хватает begin и end."

      ;-) что сказать :-) "глаз замылился". Написал "как у меня в скриптах".

      Удалить
    3. > Но это у МЕНЯ ТОЛЬКО есть ощущение, что то что КОД ВЫПОЛНЯЕТСЯ - это БОЛЕЕ ЧЕМ СТРАННО?
      Всё-равно не вижу ничего особенного. Обычный привычный для Delphi баг. Ну т.е. как. ничего странного. Конечно такой код он не должен выполняться. Конечно должна быть ошибка. И она кстати иногда и бывает. Но не всегда. Поэтому иногда имеет смысл писать Assert(assigned(Self));

      Вот пример аналогичного бага использующего уже освобожденный объект без какой-либо ошибки. Проверял в Delphi 6 (с отключенной оптимизацией!).
      https://gist.github.com/tdelphi/8609918

      Причём, если заменить присвоение Delimiter на вызов Add - то тогда получим AV.

      Удалить
    4. "Поэтому иногда имеет смысл писать Assert(assigned(Self));"

      Это со всеобщим то "трендом отрицания FreeAnNil" ;-) Как же "Бауэр сказал, а Ходжес - подпел" :-)

      "Поэтому иногда имеет смысл писать Assert(assigned(Self));"
      -- был бы FreeAndNil - вопросов бы не было. И Delphi тут ВООБЩЕ непричём. Этот пример НАГЛЯДНО показывает как НЕ НАДО программировать.

      "Обычный привычный для Delphi баг."
      -- до Delphi7 включительно - "обычных" багов - не было. Лишь кривые руки прикладных программистов. А таким заявлением - вы меня пугаете.

      Удалить
    5. Чем "мой" пример отличается от "вашего"?

      Тем что за объектами программист (при желании) - может уследить. А когда появляются ссылки на анонимные методы. Да ещё и с возможностью отдачи наружу (а не только внутрь), то "уследить" становиться практически невозможно. Разве что только взять себе за правило - наружу ссылки на анонимные методы не отдавать. Только внутрь. И не хранить их нигде кроме как во входных параметрах или локальных переменных.

      Удалить
    6. > Тем что за объектами программист (при желании) - может уследить. А когда появляются ссылки на анонимные методы. Да ещё и с возможностью отдачи наружу (а не только внутрь), то "уследить" становиться практически невозможно.

      Позволю себе не согласиться. Объект - это ссылка. Ссылка на анонимный метод - это тоже ссылка. И тут уже без разницы - какую из внутренних ссылок объекта программист отправил в свободное плавание за пределы области объекта. Ссылку на анонимную процедуру, или ссылку на внутренний объект. Сам отправил - сам и следи. Только надо учитывать, что Delphi может не предупредить программиста о том, что используемая им ссылка указывает на освобожденный объект.
      Просто

      Удалить