这是代码TCustomLayer.Destroy
destructor TCustomLayer.Destroy;
var
I: Integer;
begin
if Assigned(FFreeNotifies) then
begin
for I := FFreeNotifies.Count - 1 downto 0 do
begin
TCustomLayer(FFreeNotifies[I]).Notification(Self);
if FFreeNotifies = nil then Break;
end;
FFreeNotifies.Free;
FFreeNotifies := nil;
end;
SetLayerCollection(nil); <<-- bug, see below.
inherited; <<---- See note below.
end;
请注意,在SetLayerCollection
.
错误代码
procedure TCustomLayer.SetLayerCollection(Value: TLayerCollection);
begin
if FLayerCollection <> Value then begin
if Assigned(FLayerCollection) then begin
if FLayerCollection.MouseListener = Self then
FLayerCollection.MouseListener := nil;
FLayerCollection.RemoveItem(Self);
end;
if Assigned(Value) then Value.InsertItem(Self);
end;
/// FLayerCollection is never set!
end;
该行SetLayerCollection(nil);
实际上并没有设置 LayerCollection!内部 FLayerCollection 可能会遇到某种use after free
情况,这可能是您正在发生的事情。
像这样更改代码SetLayerCollection
:
错误修复
procedure TCustomLayer.SetLayerCollection(Value: TLayerCollection);
begin
if FLayerCollection <> Value then begin
if Assigned(FLayerCollection) then begin
if FLayerCollection.MouseListener = Self then begin
FLayerCollection.MouseListener := nil;
end;
FLayerCollection.RemoveItem(Self);
end;
if Assigned(Value) then begin
Value.InsertItem(Self)
end;
FLayerCollection:= Value; // add this line.
end;
end;
注意
我的假设是以下代码段会导致错误:
SetLayerCollection(nil);
inherited;
SetLayerCollection(value);
使 FLayerCollection 保持不变。
析构函数以inherited
某种方式调用与LayerCollection
.
让我知道这是否可以解决错误。
我提交了一个新问题:https ://github.com/graphics32/graphics32/issues/13
更新:问题因 TPointerMap.Delete 中
的一个错误而关闭实际问题在这里:
https ://github.com/graphics32/graphics32/issues/14
TPointerMap.Delete 的代码不正确:
function TPointerMap.Delete(BucketIndex, ItemIndex: Integer): PData;
begin
with FBuckets[BucketIndex] do
begin
Result := Items[ItemIndex].Data;
if FCount = 0 then Exit; <<-- error: how can result be valid if count = 0?
Dec(Count);
if Count = 0 then
SetLength(Items, 0)
else
if (ItemIndex < Count) then
//Oops off by 1 error! ---------------------------------------VVVVV
Move(Items[ItemIndex + 1], Items[ItemIndex], (Count - ItemIndex - 1) * SizeOf(TPointerBucketItem));
end;
Dec(FCount); <<-- The use of with makes this statement confusing.
end;
代码应更改如下:
function TPointerMap.Delete(BucketIndex, ItemIndex: Integer): PData;
var
Bucket: TPointerBucket ;
begin
if FCount = 0 then Exit(nil);
//Perhaps add some code to validate BucketIndex & ItemIndex?
Assert(BucketIndex < Length(FBuckets));
Bucket:= FBuckets[BucketIndex];
if ItemIndex >= Bucket.
Assert(ItemIndex < Length(Bucket.Items));
Result := Bucket.Items[ItemIndex].Data;
Dec(Bucket.Count);
if Bucket.Count = 0 then
SetLength(Bucket.Items, 0)
else
/// assume array like so: 0 1 2 3 4 , itemindex = 0
/// result should be 1 2 3 4
/// move(1,0,4) (because 4 items should be moved.
/// Thus move (itemindex+1, intemindex, count-itemindex)
if (ItemIndex < Bucket.Count) then
Move(Items[ItemIndex + 1], Items[ItemIndex], (Bucket.Count - ItemIndex) * SizeOf(TPointerBucketItem));
end;
Dec(FCount);
end;