Как безопасно создавать и удалять несколько объектов в Delphi
Как вы можете безопасно создавать и освобождать несколько объектов?
В принципе, такого рода вещи:
newOrderSource := TWebNewOrderSource.Create();
twData := TTWData.Create();
webData := TWebData.Create();
try
//do stuff
finally
newOrderSource.Free();
twData.Free();
webData.Free();
end;
В этом случае вторая и третья команды create небезопасны, поскольку они работают с базой данных. Должен ли я просто поместить все создатели в блок try и проверить, назначены ли они до того, как я позвоню бесплатно?
Ответы
Ответ 1
Вы можете сделать это с помощью одного блока try, если назначить nil первым, как,
newOrderSource := nil;
twData := nil;
webData := nil;
try
newOrderSource := TWebNewOrderSource.Create();
twData := TTWData.Create();
webData := TWebData.Create();
//do stuff
finally
webData.Free();
twData.Free();
newOrderSource.Free();
end;
Это работает, потому что Free()
проверяет Self
на nil
.
Ответ 2
Как я уверен, все знают, стандартный способ управления объектом выглядит следующим образом:
A := TMyObject.Create;
try
A.DoSomething;
finally
A.Free;
end;
Если в TMyObject.Create
есть исключение, тогда будет вызван деструктор, а затем возникнет исключение. В этом случае A
не будет назначено.
Когда у вас есть несколько объектов, вы можете повторить шаблон:
A := TMyObject.Create;
try
B := TMyObject.Create;
try
A.DoSomething;
B.DoSomething;
finally
B.Free;
end;
finally
A.Free;
end;
Это очень быстро становится беспорядком и, следовательно, вопросом.
Стандартный трюк заключается в том, чтобы использовать тот факт, что Free
можно смело вызывать на объектной ссылке nil
.
A := nil;
B := nil;
try
A := TMyObject.Create;
B := TMyObject.Create;
A.DoSomething;
B.DoSomething;
finally
B.Free;
A.Free;
end;
У этого есть незначительная слабость, что он не устойчив к исключениям, возникающим в B.Free
, но неразумно рассматривать это как условие отказа, которое можно игнорировать. Деструкторы не должны создавать исключения. Если они это сделают, значит, ваша система, вероятно, будет беспорядочно сломана.
Этот шаблон выше может стать немного грязным, поскольку добавлено больше объектов, поэтому я лично использую следующие вспомогательные методы.
procedure InitialiseNil(var Obj1); overload;
procedure InitialiseNil(var Obj1, Obj2); overload;
procedure InitialiseNil(var Obj1, Obj2, Obj3); overload;
procedure FreeAndNil(var Obj1); overload;
procedure FreeAndNil(var Obj1, Obj2); overload;
procedure FreeAndNil(var Obj1, Obj2, Obj3); overload;
На самом деле мой код имеет версии с еще большим количеством параметров. Для простоты обслуживания этот код автоматически генерируется из короткого Python script.
Эти методы реализованы очевидным образом, например
procedure FreeAndNil(var Obj1, Obj2);
var
Temp1, Temp2: TObject;
begin
Temp1 := TObject(Obj1);
Temp2 := TObject(Obj2);
Pointer(Obj1) := nil;
Pointer(Obj2) := nil;
Temp1.Free;
Temp2.Free;
end;
Это позволяет нам перезаписать образец выше:
InitialiseNil(A, B);
try
A := TMyObject.Create;
B := TMyObject.Create;
A.DoSomething;
B.DoSomething;
finally
FreeAndNil(B, A);
end;