По мотивам - Коротко. Контроль за созданием/освобождением объектов. Только код. №3
Теперь мы прикручиваем контроль за зомби-объектами.
Их список мы ведём в f_DefferedObjects : TmsDefferedObjects.
Репозитарий:
https://bitbucket.org/ingword/mindstream/src/ee7a505dc542a2d71487a1b0e06ff2dc5827f9f1/Core/msCoreObjects.pas?at=MS-6_AddTestToMindStream
Код:
Теперь мы прикручиваем контроль за зомби-объектами.
Их список мы ведём в f_DefferedObjects : TmsDefferedObjects.
Репозитарий:
https://bitbucket.org/ingword/mindstream/src/ee7a505dc542a2d71487a1b0e06ff2dc5827f9f1/Core/msCoreObjects.pas?at=MS-6_AddTestToMindStream
Код:
unit msCoreObjects;
interface
uses
System.Classes,
System.Generics.Collections
;
type
TmsLog = class;
TmsLogLambda = reference to procedure (aLog: TmsLog);
TmsLog = class
strict private
f_FS : TFileStream;
public
class procedure Log(const aFileName: String; aLambda: TmsLogLambda);
constructor Create(const aFileName: String);
destructor Destroy; override;
procedure ToLog(const aString: AnsiString);
end;//TmsLog
TmsClassInstanceCount = record
public
rCount : Integer;
rMaxCount : Integer;
constructor Create(aCount: Integer);
constructor IncCreate(const anOther: TmsClassInstanceCount);
constructor DecCreate(const anOther: TmsClassInstanceCount);
end;//TmsClassInstanceCount
TmsClassInstanceCountList = TDictionary<String, TmsClassInstanceCount>;
// ms-help://embarcadero.rs_xe7/libraries/System.Generics.Collections.TDictionary.html
TmsDefferedObjects = class(TList<TObject>)
// - список отложенных объектов
// http://programmingmindstream.blogspot.ru/2014/11/blog-post_8.html
public
destructor Destroy; override;
end;//TmsDefferedObjects
TmsObjectsWatcher = class
// - следилка за объектами
// НЕ является ПОТОКОБЕЗОПАСНОЙ
private
class var f_ObjectsCreatedCount : Integer;
class var f_ObjectsCreated: TmsClassInstanceCountList;
class var f_DefferedObjects : TmsDefferedObjects;
public
class procedure CreateObject(aClass: TClass; var theInstance: TObject);
class procedure DestroyObject(anObject: TObject);
class procedure ObjectCreated(anObject: TObject);
class procedure ObjectDestroyed(anObject: TObject);
class destructor Destroy;
end;//TmsObjectsWatcher
TmsWatchedObject = class abstract(TObject)
// - Класс, который умеет контроллировать создание/уничтожение своих экземпляров
public
class function NewInstance: TObject; override;
// ms-help://embarcadero.rs_xe7/libraries/System.TObject.NewInstance.html
procedure FreeInstance; override;
// ms-help://embarcadero.rs_xe7/libraries/System.TObject.FreeInstance.html
end;//TmsWatchedObject
TmsStringList = class abstract(TStringList)
// - Класс, который умеет контроллировать создание/уничтожение своих экземпляров
public
class function NewInstance: TObject; override;
// ms-help://embarcadero.rs_xe7/libraries/System.TObject.NewInstance.html
procedure FreeInstance; override;
// ms-help://embarcadero.rs_xe7/libraries/System.TObject.FreeInstance.html
end;//TmsStringList
TmsInterfacedNonRefcounted = class abstract(TmsWatchedObject)
// - реализация объектов реализующих интерфейсы, но БЕЗ подсчёта ссылок
// т.е. присваиваемый объект - НЕ ЗАХВАТЫВАЕТСЯ и "владелец" - НЕ УПРАВЛЯЕТ временем жизни
// Зачем? Чтобы избежать кросс-ссылок.
// От TmsInterfacedNonRefcounted должны наследоваться объекты-контейнеры,
// которые хотят сообщать своим "детям" свои интерфейсы.
//
// Тут есть одна ТОНКОСТЬ - объект-контейнер - в СВОЮ очередь может являться
// "ребёнком", но мы это потом - РАЗРУЛИМ, когда дойдём.
protected
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
end;//TmsInterfacedNonRefcounted
TmsInterfacedRefcounted = class abstract(TInterfacedObject)
// Реализация объектов, реализующих интерфейсы. С ПОДСЧЁТОМ ссылок.
//
// НЕ САМАЯ хорошая реализация, лучше реализация тут - http://18delphi.blogspot.ru/2013/04/iunknown.html
// но в учётом ARC - пользуемся пока "нативной реализаией"
//
// Таже ещё есть вот что "почитать":
// - http://18delphi.blogspot.ru/2013/07/blog-post_3683.html
// - http://18delphi.blogspot.ru/2013/07/1.html
// - http://18delphi.blogspot.ru/2013/07/2.html
// - http://18delphi.blogspot.ru/2013/07/2_18.html
// - http://18delphi.blogspot.ru/2013/07/blog-post_8789.html
public
class function NewInstance: TObject; override;
// ms-help://embarcadero.rs_xe7/libraries/System.TObject.NewInstance.html
procedure FreeInstance; override;
// ms-help://embarcadero.rs_xe7/libraries/System.TObject.FreeInstance.html
end;//TmsInterfacedRefcounted
implementation
uses
System.SysUtils,
Math
;
// TmsClassInstanceCount
constructor TmsClassInstanceCount.Create(aCount: Integer);
begin
rCount := aCount;
rMaxCount := rCount;
end;
constructor TmsClassInstanceCount.IncCreate(const anOther: TmsClassInstanceCount);
begin
Self := anOther;
Inc(rCount);
rMaxCount := Max(anOther.rMaxCount, rCount);
end;
constructor TmsClassInstanceCount.DecCreate(const anOther: TmsClassInstanceCount);
begin
Self := anOther;
Dec(rCount);
end;
destructor TmsDefferedObjects.Destroy;
var
l_Object : TObject;
begin
for l_Object in Self do
FreeMem(Pointer(l_Object), l_Object.InstanceSize);
inherited;
end;
// TmsObjectsWatcher
class procedure TmsObjectsWatcher.CreateObject(aClass: TClass; var theInstance: TObject);
begin
GetMem(Pointer(theInstance), aClass.InstanceSize);
// - распределяем память подобъекты сами. Зачем?
// Чтобы следить за повторным удалением.
// http://programmingmindstream.blogspot.ru/2014/11/blog-post_8.html
aClass.InitInstance(theInstance);
ObjectCreated(theInstance);
end;
class procedure TmsObjectsWatcher.DestroyObject(anObject: TObject);
const
cMaxDefferedObjectsCount = 1000;
var
l_P : Pointer;
begin
if (f_DefferedObjects <> nil) then
if (f_DefferedObjects.IndexOf(anObject) >= 0)then
raise Exception.Create('Объект класса ' + anObject.ClassName + ' уже был освобождён');
ObjectDestroyed(anObject);
anObject.CleanupInstance;
if (f_DefferedObjects = nil) then
f_DefferedObjects := TmsDefferedObjects.Create;
f_DefferedObjects.Add(anObject);
if (f_DefferedObjects.Count > cMaxDefferedObjectsCount) then
begin
l_P := f_DefferedObjects.First;
FreeMem(l_P);
f_DefferedObjects.Delete(0);
end;//f_DefferedObjects.Count > cMaxDefferedObjectsCount
//FreeMem(Pointer(anObject));
end;
class procedure TmsObjectsWatcher.ObjectCreated(anObject: TObject);
var
l_ClassName : String;
begin
Inc(f_ObjectsCreatedCount);
if (f_ObjectsCreated = nil) then
f_ObjectsCreated := TmsClassInstanceCountList.Create;
l_ClassName := anObject.ClassName;
if (not f_ObjectsCreated.ContainsKey(l_ClassName)) then
f_ObjectsCreated.Add(l_ClassName, TmsClassInstanceCount.Create(1))
else
f_ObjectsCreated.Items[l_ClassName] := TmsClassInstanceCount.IncCreate(f_ObjectsCreated.Items[l_ClassName]);
end;
class procedure TmsObjectsWatcher.ObjectDestroyed(anObject: TObject);
var
l_ClassName : String;
begin
Assert(f_ObjectsCreatedCount > 0, 'Какие-то объекты уже были освобождены несколько раз');
if (f_ObjectsCreated <> nil) then
begin
l_ClassName := anObject.ClassName;
if f_ObjectsCreated.ContainsKey(l_ClassName) then
f_ObjectsCreated.Items[l_ClassName] := TmsClassInstanceCount.DecCreate(f_ObjectsCreated.Items[l_ClassName]);
end;//f_ObjectsCreated <> nil
Dec(f_ObjectsCreatedCount);
end;
class procedure TmsLog.Log(const aFileName: String; aLambda: TmsLogLambda);
var
l_Log : TmsLog;
begin
l_Log := Create(aFileName);
try
aLambda(l_Log);
finally
FreeAndNil(l_Log);
end;//try..finally
end;
constructor TmsLog.Create(const aFileName: String);
begin
inherited Create;
f_FS := TFileStream.Create(aFileName, fmCreate);
end;
destructor TmsLog.Destroy;
begin
FreeAndNil(f_FS);
inherited;
end;
procedure TmsLog.ToLog(const aString: AnsiString);
const
cEOL : ANSIString = #13#10;
begin//OutLn
f_FS.Write(aString[1], Length(aString));
f_FS.Write(cEOL[1], Length(cEOL));
end;//OutLn
class destructor TmsObjectsWatcher.Destroy;
begin
if (f_ObjectsCreated <> nil) then
if (f_ObjectsCreated.Count > 0) then
begin
// Далее выводим статистику неосвобождённых объектов в лог:
TmsLog.Log(ParamStr(0) + '.objects.log',
procedure (aLog: TmsLog)
var
l_Key : String;
l_Value : TmsClassInstanceCount;
begin
aLog.ToLog('Неосвобождено объектов: ' + IntToStr(f_ObjectsCreatedCount));
for l_Key in f_ObjectsCreated.Keys do
begin
l_Value := f_ObjectsCreated[l_Key];
aLog.ToLog(l_Key + ' Неосвобождено: ' + IntToStr(l_Value.rCount) + ' Максимально распределено: ' + IntToStr(l_Value.rMaxCount));
end;//for l_Key
end
);
end;//f_ObjectsCreated.Count > 0
FreeAndNil(f_ObjectsCreated);
FreeAndNil(f_DefferedObjects);
if (f_ObjectsCreatedCount > 0) then
raise Exception.Create('Какие-то объекты не освобождены: ' + IntToStr(f_ObjectsCreatedCount));
end;
// TmsWatchedObject
class function TmsWatchedObject.NewInstance: TObject;
begin
TmsObjectsWatcher.CreateObject(Self, Result);
end;
procedure TmsWatchedObject.FreeInstance;
begin
TmsObjectsWatcher.DestroyObject(Self);
end;
// TmsStringList
class function TmsStringList.NewInstance: TObject;
begin
TmsObjectsWatcher.CreateObject(Self, Result);
end;
procedure TmsStringList.FreeInstance;
begin
TmsObjectsWatcher.DestroyObject(Self);
end;
// TmsInterfacedNonRefcounted
function TmsInterfacedNonRefcounted.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
Result := E_NoInterface;
end;
function TmsInterfacedNonRefcounted._AddRef: Integer;
begin
Result := -1;
end;
function TmsInterfacedNonRefcounted._Release: Integer;
begin
Result := -1;
end;
//TmsInterfacedRefcounted
class function TmsInterfacedRefcounted.NewInstance: TObject;
begin
TmsObjectsWatcher.CreateObject(Self, Result);
TmsInterfacedRefcounted(Result).FRefCount := 1;
end;
procedure TmsInterfacedRefcounted.FreeInstance;
begin
TmsObjectsWatcher.DestroyObject(Self);
end;
end.
Комментариев нет:
Отправить комментарий