Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
Некоторые «продвинутые новички» умудряются реализовать даже вот такой «говнокод»
try
T := TObject.Create;
// работаем с Т
finally
T.Free;
end;
class function TObjectDestroyer.Create(AObj: TObject): IUnknown;
var
P: PObjectDestroyer;
begin
if AObj = nil then Exit(nil);
GetMem(P, SizeOf(TObjectDestroyer));
P^.FVTable := @VTable;
P^.FRefCount := 0;
P^.FObj := AObj;
Result := IUnknown(P);
end;Ну если вы пошли уж так дико велосипедить — зачем аллоцировать в куче? Сложите на стек все сразу и все. Это же дорогущий враппер.TMyRecord = record
VTable: Pointer;
Obj: TObject;
Intf: IUnknown;
end;
class function TObjectDestroyer.Create(AObj: TObject): TMyRecord;
begin
if AObj = nil then Exit(nil);
Result.VTable := VTable;
Result.Obj := AObj;
Result.Intf := IUnknown(@Result);
end; Я выкинул счетчик ссылок, опираясь на то, что мы TMyRecord не будем никуда передавать. Соотвественно надо будет учесть в _Release методе, что нам не надо ничего освобождать. Ну и поскольку нам таки нужна корректная финализация, а интерфейс должен лежать на стеке — добавил интерфейсную ссылку в TMyRecord. Как-то так.procedure TestWriteBySharedPtr;
var
F1, F2: TFileStream;
ConstData: DWORD;
begin
ConstData := $DEADBEEF;
F1 := TFileStream.Create('data1.bin', fmCreate);
F2 := TFileStream.Create('data2.bin', fmCreate);
TObjectDestroyer.Create(F1); //полагаемся на то, то на стеке неявно будет создана переменная интерфейсного типа
TObjectDestroyer.Create(F2); //снова полагаемся на это
F1.WriteBuffer(ConstData, SizeOf(ConstData));
F2.WriteBuffer(ConstData, SizeOf(ConstData));
//тут делфя невяно создат блок финализации, в котором вызовет Release для эти двух переменных
end;TObjectDestroyer.Create(F1);
procedure TestWriteBySharedPtr;
var
F1, F2: TFileStream;
ConstData: DWORD;
temp1, temp2: IUnknown;
begin
Pointer(temp1) := nil;
Pointer(temp2) := nil;
try
ConstData := $DEADBEEF;
F1 := TFileStream.Create('data1.bin', fmCreate);
F2 := TFileStream.Create('data2.bin', fmCreate);
temp1 := TObjectDestroyer.Create(F1); //тут будет вызван _AddRef для temp1
temp2 := TObjectDestroyer.Create(F2); //тут будет вызван _AddRef для temp2
F1.WriteBuffer(ConstData, SizeOf(ConstData));
F2.WriteBuffer(ConstData, SizeOf(ConstData));
//тут делфя невяно создат блок финализации, в котором вызовет Release для эти двух переменных
finally
temp1._Release;
temp2._Release;
//не обнуляю переменные т.к. делфя под капотом этого не делает насколько мне известно
end;
end;procedure TestWriteBySharedPtr;
var
F1, F2: TFileStream;
ConstData: DWORD;
temp1, temp2: IUnknown;
begin
Pointer(temp1) := nil;
try
ConstData := $DEADBEEF;
F1 := TFileStream.Create('data1.bin', fmCreate);
F2 := TFileStream.Create('data2.bin', fmCreate);
temp1 := TObjectDestroyer.Create(F1); //тут будет вызван _AddRef для temp1
temp1 := TObjectDestroyer.Create(F2); //тут будет вызван для temp1 сначала _Release, затем _AddRef для уже нового значения
F1.WriteBuffer(ConstData, SizeOf(ConstData));
F2.WriteBuffer(ConstData, SizeOf(ConstData));
//тут делфя невяно создат блок финализации, в котором вызовет Release для эти двух переменных
finally
temp1._Release;
//не обнуляю переменные т.к. делфя под капотом этого не делает насколько мне известно
end;
end;procedure TestWriteBySharedPtr;
var
MyObjects: array of TMyObject;
I: Integer;
begin
SetLength(MyObjects, 32);
for I := 0 to Length(MyObjects) - 1 do
begin
MyObjects[I] := TMyObject.Create;
TObjectDestroyer.Create(MyObjects[I]); //вот тут будет одна временная переменная на стеке!!!
//и она будет каждую итерацию перезаписываться, уничтожая созданные на предыдущей итерации объекты
end;
for I := 0 to Length(MyObjects) - 1 do
MyObjects[I].DoSomething(); //и как результат на первой же итерации AV, либо хуже.
//Мемори менеджер может реюзнуть освобожденное пространство,
//и объект MyObjects[0] будет ссылаться на одно и то же пространство с объектом MyObjects[2] например
end;В общем явно опасная штука.procedure TestWriteBySharedPtr2;
var
MyObjects: array of TSharedPtr<TObject>;
I: Integer;
begin
SetLength(MyObjects, 32);
for I := 0 to Length(MyObjects) - 1 do
MyObjects[I] := TSharedPtr.Create(TObject.Create);
for I := 0 to Length(MyObjects) - 1 do
Writeln(MyObjects[I].Value.ClassName);
end;
T1 := nil;
T2 := nil;
T3 := nil;
try
T1 := TObject.Create;
T2 := TObject.Create;
T3 := TObject.Create;
// работаем со всеми тремя экземплярами Т1/Т2/Т3
finally
T3.Free;
T2.Free;
T1.Free;
end;Может быть когда-нибудь разработчики делфи добавят инициализацию объектных переменных, чтобы наконец то можно было писать вот так:
try
T1 := TObject.Create;
T2 := TObject.Create;
T3 := TObject.Create;
// работаем со всеми тремя экземплярами Т1/Т2/Т3
finally
T3.Free;
T2.Free;
T1.Free;
end;А может быть и так:
type
TTest = class(TInterfacedObject)
public
destructor Destroy; override;
end;
function CreateTest: IUnknown;
begin
Result := TTest.Create;
end;
procedure TForm3.FormCreate(Sender: TObject);
var
I: Integer;
begin
for I := 0 to 3 do CreateTest;
ShowMessage('AfterLoop');
end; // последний инстанс TTest - будет тут уничтожен.
{ TTest }
destructor TTest.Destroy;
begin
ShowMessage('TTest.Destroy');
inherited;
end;
Пара слов о кэшировании данных при чтении и смартпойнтерах