我正在努力处理一些内存分配,并且在使用XE2时难以找到并修复异常。
我有一个工厂类,它创建并存储动态创建的对象以便在表单上显示,但工厂创建在某些情况下失败。如果我在程序启动后执行以下代码作为第一个操作,那么一切都很好 - 内存分配得很好,所有其他功能都可以工作。但是,如果我说打开另一个表单,或打开文件对话框,然后执行工厂创建,则有一个例外:
Exception EAccessViolation: Access violation at address 0040AA1F in module
'UMTester.exe'. Write of address 00000069 (OS Exception) Exception occurred at
$0040AA1F (Module "System", Procedure "@DynArrayAsg", Unit "", Line 0)
异常始终位于地址0040AA1F,proc始终为@DynArrayAsg。
虽然工厂将在发布时立即创建,但我不想忽略该错误,因为这只会是糟糕的形式!
这是工厂创建代码:
constructor TFactory.Create(FactoryObjectClass : tclass;Capacity : integer);
var nn : integer;
fptr : pointer;
fObj : TFactoryObject;
begin
fClass := FactoryObjectClass;
fsize := fclass.InstanceSize;
fdata:=nil;
//Capacity represents the number of objects; trying to allocate small amounts
//of memory results in exceptions so i artificially increase it anything less
//than 20 throws an exception
if Capacity<19 then Capacity:=20;
//This seems to reduce exceptions as the requested memory is in whole blocks
while frac((Capacity*SizeOf(Pointer))/4)<>0.0 do Inc(Capacity);
fcapacity := Capacity;
//Allocate memory
getmem(fdata,fsize*fcapacity);
getmem(fDatalist,sizeof(Pointer)*fcapacity);
getmem(ffreelist,sizeof(Pointer)*fcapacity);
fdatacount :=0;
ffreecount :=0;
fptr := fdata;
//create a pointer to the factory object at each memory address
for nn := 0 to Capacity-1 do begin
fdatalist[fdatacount]:= fptr; <------- Exception is always here
fobj := Fclass.InitInstance(fptr) as TFactoryObject;
fobj.factory := self;
fObj.Create;
fptr := pointer(integer(fptr)+fsize);
inc(fDataCount);
end;
end;
工厂指针列表是从TDictionary实例化的,具有基本表单元素,如标签,备忘录和列表框等。
procedure TObjs.Initialize(AObjs : TDict);
var
nn :integer;
C : TRttiContext;
T : TRttiInstanceType;
V : TValue;
begin
//Process all objects requested
for nn := 0 to AObjs.Count-1 do begin
//Locate the class from the supplied reference in AObjs
T := (C.GetType(TClass(FindClass(AObjs[nn]['Type']))) as TRttiInstanceType);
//Invoke the creation of the object by calling its native constructor
V := T.GetMethod('Create').Invoke(T.metaClassType,[Application]);
//Adds each requested object to the repository and displays the object
Objs[nn].SetValue(V.AsObject,AObjs[nn]);
end;
end;
观察与思考:
工厂在这里创建一次:
SetLength(Objs,AObjs.Count);
AFactory := TFactory.Create(TchObj,AObjs.Count);
//Initialize the full array of objects
for Index := 0 to AObjs.Count -1 do
Objs[Index] := Afactory.Request_obj as TchObj;
TFactory的类定义是:
TchFactory = class
private
fdata : pointer;
fsize : integer;
fDataList : PPointerList; // from classes unit
fdatacount : integer;
fFreeList : PPointerList;
fFreeCount : integer;
fCapacity : integer;
fcLass : TClass;
public
constructor Create(FactoryObjectClass : Tclass;Capacity : integer);
destructor Destroy; override;
function Request_Obj : TchFactoryObject;
procedure Recycle(FactoryObject : TchFactoryObject);
property Capacity : integer read fCapacity;
property CountUsed : integer read fdataCount;
property CountFree : integer read fFreeCount;
end;
我能看到什么?
关于我如何调试的任何想法?
或者我只是做了一些疯狂的错误?
修改 删除David引用的“试错”行并将GetMem更改为AllocMem修复了该问题。所以工厂构造函数的最终代码是:
constructor TchFactory.Create(FactoryObjectClass : tclass;Capacity : integer);
var Index : integer;
fptr : pointer;
fObj : TchFactoryObject;
begin
fClass := FactoryObjectClass;
fsize := fclass.InstanceSize;
fcapacity := Capacity;
fdata := AllocMem(fsize*fcapacity);
fdatalist:= AllocMem(sizeof(Pointer)*fcapacity);
fFreelist:= AllocMem(sizeof(Pointer)*fcapacity);
fdatacount :=0;
ffreecount :=0;
fptr := fdata;
for index := 0 to Capacity-1 do begin
fdatalist[fdatacount]:= fptr;
fobj := Fclass.InitInstance(fptr) as TchFactoryObject;
fobj.factory := self;
fObj.Create;
fptr := pointer(integer(fptr)+fsize);
inc(fDataCount);
end;
end;
答案 0 :(得分:3)
我只是做了一些疯狂的错误?
是。疯狂错了。要实例化一个实例,您应该只需调用构造函数。你想一次创建一堆吗?运行一个循环并在该循环中调用构造函数。
可以实例化内存中连续的对象,但这需要技巧和理解。它涉及依赖实施细节。如果有合理的性能原因,您应该只尝试这样做。从我所看到的情况并非如此。因此,以通常的方式实例化实例的简单for循环就是您所需要的。
答案 1 :(得分:0)
Afaik这是允许的,如果没有完整声明,很难看出出了什么问题。 但是你必须确保你的记忆是零初始化的。
如果班级有自动类型,那么这是必需的。