Delphi и доработка в юнит - PullRequest
       67

Delphi и доработка в юнит

4 голосов
/ 20 февраля 2010

У меня есть два блока unitA и unitB. Класс TFoo объявлен в unitB.

Всегда ли безопасно вызывать B.Free при доработке блока A?

Как это зависит от того, в каком порядке unitA и unitB находятся в dpr?

Могу ли я быть уверен, что unitB существует, когда выполняется финализация unitA?

unit unitB;
interface
type
 TFoo = class
   // code...
  end;
 // code....
end;

unit unitA;
// code..
implementation
uses
 unitB;
var
 A: TStringList;
 B: UnitB.TFoo;

initialization
 A:= TStringList.Create;
 B:= UnitB.TFoo.Create;
finalization
 A.Free;
 B.Free;  // Is it safe to call?
end.

Ответы [ 6 ]

16 голосов
/ 20 февраля 2010

Да, с вами все будет в порядке, так как B создается в модуле A. Правило состоит в том, что разделы инициализации вызываются на основе порядка, в котором они находятся в DPR, если только один из модулей не ссылается на другой модуль. В этом случае указанный блок инициализируется первым. Завершение в обратном порядке.

В вашем случае у Б нет секции инициализации, поэтому это спорный вопрос. Однако при выполнении раздела инициализации модуля A он будет использовать определение TFoo в модуле B.

Еще одно предупреждение о разделах инициализации и финализации - они происходят вне глобального обработчика исключений. Любое исключение, которое происходит там, просто завершает приложение. Поэтому отслеживание и отладка этих исключений может быть проблемой в больших программах. Вы можете использовать свою собственную регистрацию исключений, просто чтобы быть уверенным.

4 голосов
/ 20 февраля 2010

НЕТ. Вы можете попробовать, можете надеяться, но нет гарантии в порядке вызова инициализации и финализации. См. qc72245 , qc56034 и и многие другие .

UPDATE:

  1. раздел финализации выполняется в порядке, обратном инициализации. Ваш пример безопасен, у вас нет зависимости от вызова разделов инициализации между модулями
  2. Delphi может смешивать вызывающие единицы (пункт 1 остается в силе, разделы инициализации и финализации меняются местами)

Пример: * * тысяча двадцать-пять

unitA // no dependency on unitB
var SomeController;
initialization
  SomeController := TSomeController.Create;
finalization
  SomeController.Free;

unitB
uses
  unitA;
initialization
  SomeController.AddComponent(UnitBClass);
finalization
  SomeController.RemoveComponent(UnitBClass);

Общий (правильный) порядок (99,99%) звонков:

  1. unitA.initialization
  2. unitB.initialization
  3. бежать ...
  4. unitB.finalization
  5. unitA.finalization

Но иногда может неправильно скомпилировать файл Delphi:

  1. unitB.initialization - AV здесь
  2. unitA.initialization
  3. бежать ...
  4. unitA.finalization
  5. unitB.finalization - и здесь тоже

Маленькая оффтоп история:

У нас довольно большой проект: Type1 в Unit1, Type2 = class (Type1) в Unit2. Файлы упорядочены в project.dpr и после нескольких лет с добавлением Unit200 ( нет зависимости с unit1 / 2 ). Delphi начинает компилировать проект с Unit2.Initialization до Unit1.Initialization. Единственным безопасным решением является вызов собственных функций Init из раздела инициализации.

3 голосов
/ 22 ноября 2014

В конкретной ситуации, которую вы показываете здесь, вы будете в порядке. Но это не займет столько рефакторинга, прежде чем он начнет работать неправильно.

Delphi неплохо справляется с задачей обеспечения того, чтобы юниты оставались в памяти до тех пор, пока они необходимы. Но он может сделать это только в том случае, если знает, что нужен юнит.

Мой классический пример по теме - это блок, содержащий только список объектов

unit Unit1;

interface
uses
  Contnrs;
var
  FList : TObjectList;
implementation

initialization
  FList := TObjectList.Create(True);
finalization
  FList.Free;
end.

Unit1 явно зависит только от Contnrs. Delphi будет только гарантировать, что модуль Contnrs (и, вероятно, также «зависимые» модули, хотя я не уверен на 100%) все еще загружен в память. Если TForm добавлен в список, модуль Forms может быть уже завершен при вызове FList.free, он потерпит крах, когда попытается освободить содержащуюся TForm. Delphi не может знать, что для Unit1 требуется модуль Forms. В этом конкретном случае это будет зависеть от того, какие единицы заказа объявлены в dpr.

3 голосов
/ 20 февраля 2010

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

Но лучшим способом может быть объявление переменной в модуле B и ее инициализация / финализация в B.Поскольку инициализация происходит до вызова любого другого кода, он будет инициализирован до того, как он будет сделан доступным для модуля A, если он объявлен в предложении использования модуля A.

Еще один шаг, который вы могли бы рассмотреть, - этопереместив единичную переменную B на один шаг вперед и сделав ее вызовом функции для загрузки по требованию, но это также может зависеть от вашего использования.

например

unit unitB;
interface
type
 TFoo = class
   // code...
  end;
 // code....
 function UnitVarB:TFoo;

implementation

var
  gUnitVarB : TFoo;  

function UnitVarB:TFoo 
begin
  if not assigned(gUnitVarB) then
    gUnitVarB := TFoo.Create;

  result := gUnitVarB;
end;

finalization
  if assigned(gUnitVarB) then
    gUnitVarB.free;  //or FreeAndNil(gUnitVarB);

end;

unit unitA;

// code..
implementation

uses
 unitB;

var
 A: TStringList;

//code...
  ...UnitVarB....
//code...

initialization
 A:= TStringList.Create;
finalization
 A.Free;
end.

Мне кажетсяпомнить где-то, что инициализация модуля может быть дорогой в том смысле, что если модуль, на который вы больше не ссылаетесь напрямую, все еще находится в предложении использования во время компиляции, умный компоновщик не удалит его из-за раздела инициализации.Хотя это может показаться не таким уж плохим, если бы у каждого устройства была секция инициализации, тогда большинство программ на Delphi были бы на НАМНОГО больше, чем они уже есть.

Я не говорю, что не используйте их, но мое правило - использовать их экономно.

Ваш первоначальный пример кода нарушает это правило.Я думал, что упомяну это.

Райан

1 голос
/ 20 февраля 2010

В духе полного раскрытия, я не разрабатывал в Delphi с 2005 года. Однако я разрабатывал в Delphi исключительно начиная с Delphi 1 в 1996 году и был сертифицирован в Delphi 5 в 2001 году. При этом мое использование раздел финализации был редок. Единственный раз, когда я бы использовал его, это если бы мне нужно было установить что-то особенное в .dpr. Обычно это происходит только в том случае, если я занимаюсь разработкой пользовательских компонентов, и есть некоторые зависимости, которыми мне нужно управлять с помощью других пользовательских компонентов, которые я разрабатываю.

Для типичной разработки приложений я держался в стороне от раздела инициализации / финализации и просто использовал шаблоны проектирования, такие как синглтоны, фасады и фабрики, для управления созданием и управлением моими классами. Встроенный сборщик мусора был достаточно хорош для 98,5% моих проектов.

Чтобы ответить на ваш вопрос, вам необходимо установить зависимость от TFoo в вашем коде UnitA и, как предложил Райан, убедиться, что он назначен до уничтожения. При этом я призываю вас убедиться, что использование раздела инициализации / финализации необходимо, прежде чем уделять ему слишком много времени.

1 голос
/ 20 февраля 2010

Да, это безопасно. Вы можете упростить работу компилятора, объявив UnitB перед UnitA в файле dpr, но компилятор разрешит ссылки в любом случае.

...