Delphi 无效的指针操作--don';我不知道为什么

Delphi 无效的指针操作--don';我不知道为什么,delphi,Delphi,我一直在努力解决一个问题,并开发了一个示例应用程序,它显示了(或多或少——错误发生了,但它位于不同的位置)我遇到的问题 这段代码的思想是创建一个对象TGenericList,其中包含一个包含不同类型数据(例如整数、双精度、记录等)的通用对象列表。当其中一个对象发生更改时,它应通知保存该对象的列表 示例程序在运行时,会在第行显示一个EInvalidPointer异常 L.Free; ABase.RegisterObserver(Self); 在应用程序的末尾 在调试器中跟踪时,在TInterf

我一直在努力解决一个问题,并开发了一个示例应用程序,它显示了(或多或少——错误发生了,但它位于不同的位置)我遇到的问题

这段代码的思想是创建一个对象TGenericList,其中包含一个包含不同类型数据(例如整数、双精度、记录等)的通用对象列表。当其中一个对象发生更改时,它应通知保存该对象的列表

示例程序在运行时,会在第行显示一个EInvalidPointer异常

L.Free;
ABase.RegisterObserver(Self);
在应用程序的末尾

在调试器中跟踪时,在TInterfacedObject例程中引发异常:

procedure TInterfacedObject.BeforeDestruction;
begin
  if RefCount <> 0 then
    Error(reInvalidPtr);
end;
在TGenericList.AddBase()中,我没有得到异常。还要注意的是,我甚至还没有实现变更通知方法,因此观察者列表从未真正使用过——它只是存在并保存对象引用。在这种情况下,一个

我能想到的唯一一件事是,TList以某种方式释放了观察家,因此当我称L.Free时,它已经是Free'd了。我不认为TList会这么做。帮助文件说TObjectList可以。然后,它似乎永远不会到达列表空闲的那一行,这将释放TBase对象

我编译时没有收到任何警告

我正在运行Delphi-Tokyo(10.2),社区版

program GenericTest;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, System.Generics.Collections;

type
  IObserver = interface
    ['{DD9243B9-0722-486A-B4BF-0929AB5B6627}']
    procedure ObservableChanged(Sender : TObject);
  end;

  IObservable = interface
    ['{39EA6448-6636-40F4-B618-740B0BB28127}']
    procedure RegisterObserver(Observer : IObserver);
    procedure UnregisterObserver(Observer : IObserver);
  end;

  TBase = class(TInterfacedObject, IObservable)
  private
    FName : String;
    FObservers : TList<IObserver>;
  public
    constructor Create(AName : String);
    destructor Destroy; override;
    procedure RegisterObserver(Observer : IObserver);
    procedure UnregisterObserver(Observer : IObserver);
    property Name : String read FName;
  end;

  TGenericBase = TBase;

  TGenericBase<T> = class(TGenericBase)
  private
    FData : T;
  public
    constructor Create(AName : String);
    constructor CreateValue(AName : String; AValue : T);
    property Data : T read FData write FData;
  end;

  TGenericList = class(TInterfacedObject, IObserver)
  private
    FBases : TObjectDictionary<String, TBase>;
  public
    constructor Create;
    destructor Destroy; override;
    procedure AddBase(ABase : TBase);
    function GetBase<T: TBase>(AName : String) : T;
    procedure ObservableChanged(Sender : TObject);
  end;

//
// TBase
//
constructor TBase.Create(AName: string);
begin
  inherited Create;
  FObservers := TList<IObserver>.Create();
  FName := AName;
end;

destructor TBase.Destroy;
begin
  if (FObservers <> nil) then FObservers.Free;
end;

procedure TBase.RegisterObserver(Observer : IObserver);
begin
  if (FObservers <> nil) then FObservers.Add(Observer);
end;

procedure TBase.UnregisterObserver(Observer : IObserver);
begin
  if (FObservers <> nil) then FObservers.Remove(Observer);
end;

//
// TGenericBase<T>
//
constructor TGenericBase<T>.Create(AName : String);
begin
  inherited Create(AName);
  FData := Default(T);
end;

constructor TGenericBase<T>.CreateValue(AName : String; AValue : T);
begin
  inherited Create(AName);
  FData := AValue;
end;

//
// TGenericList
//
constructor TGenericList.Create;
begin
  inherited Create;
  FBases := TObjectDictionary<String, TBase>.Create([doOwnsValues], 32);
end;

destructor TGenericList.Destroy;
begin
  if (FBases <> nil) then FBases.Free;
  inherited Destroy;
end;

procedure TGenericList.AddBase(ABase : TBase);
begin
  FBases.Add(ABase.Name, ABase);
  // Comment out this line and the error doesn't occur.
  ABase.RegisterObserver(Self);
end;

function TGenericList.GetBase<T>(AName : String) : T;
var C : TBase;
begin
  if not FBases.TryGetValue(AName, C) then
    raise Exception.Create('Couldn''t get base.');

  Result := C as T;
end;

procedure TGenericList.ObservableChanged(Sender : TObject);
begin
  WriteLn((Sender as TGenericBase).Name);
end;

//
//
//
var C : TGenericBase;
    L : TGenericList;
    K : Integer;
    D : TGenericBase<Double>;
begin
  try
    L := TGenericList.Create;
    try
      for K := 0 to 10 do begin
        C := TGenericBase<Double>.CreateValue(IntToStr(K), K);
        L.AddBase(C);
      end;

      for K := 0 to 10 do begin
        D := L.GetBase<TGenericBase<Double>>(IntToStr(K));
        WriteLn(D.Data);
      end;

    finally
      L.Free;
    end;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  ReadLn;
end.
程序通用测试;
{$APPTYPE控制台}
{$R*.res}
使用
System.SysUtils、System.Generics.Collections;
类型
IObserver=接口
[{DD9243B9-0722-486A-B4BF-0929AB5B6627}]
观察到的程序已更改(发送方:TObject);
结束;
IObservable=接口
[{39EA6448-6636-40F4-B618-740B0BB28127}]
程序注册观察者(观察者:IObserver);
程序取消注册观察员(观察员:IObserver);
结束;
TBase=类(TInterfacedObject,IObservable)
私有的
FName:字符串;
fobserver:TList;
公众的
构造函数创建(AName:String);
毁灭者毁灭;推翻
程序注册观察者(观察者:IObserver);
程序取消注册观察员(观察员:IObserver);
属性名称:字符串读取FName;
结束;
TGenericBase=TBase;
TGenericBase=class(TGenericBase)
私有的
FData:T;
公众的
构造函数创建(AName:String);
构造函数CreateValue(AName:String;AValue:T);
属性数据:T读FData写FData;
结束;
TGenericList=class(TInterfacedObject,IObserver)
私有的
FBases:TObjectDictionary;
公众的
构造函数创建;
毁灭者毁灭;推翻
程序AddBase(ABase:TBase);
函数GetBase(AName:String):T;
观察到的程序已更改(发送方:TObject);
结束;
//
//t基地
//
构造函数TBase.Create(AName:string);
开始
继承创造;
fobserver:=TList.Create();
FName:=AName;
结束;
析构函数TBase.Destroy;
开始
如果(FObservers nil),则FObservers.Free;
结束;
程序TBase.RegisterObserver(观察者:IObserver);
开始
如果(FObservers nil),则FObservers.Add(Observer);
结束;
程序TBase.UnregisteredObserver(观察者:IObserver);
开始
如果为(FObservers nil),则为FObservers.Remove(Observer);
结束;
//
//TGenericBase
//
构造函数TGenericBase.Create(AName:String);
开始
继承创建(AName);
FData:=默认值(T);
结束;
构造函数TGenericBase.CreateValue(AName:String;AValue:T);
开始
继承创建(AName);
FData:=AValue;
结束;
//
//总目录
//
构造函数TGenericList.Create;
开始
继承创造;
FBases:=TObjectDictionary.Create([DoownsValue],32);
结束;
析构函数TGenericList.Destroy;
开始
如果(FBases nil),则FBases.Free;
继承性破坏;
结束;
程序TGenericList.AddBase(ABase:TBase);
开始
FBases.Add(ABase.Name,ABase);
//注释掉这一行,就不会出现错误。
注册观察者(Self);
结束;
函数TGenericList.GetBase(AName:String):T;
var C:TBase;
开始
如果不是FBases.TryGetValue(AName,C),则
引发异常。创建('无法获取基');
结果:=C为T;
结束;
程序TGenericList.observeChanged(发送方:ToObject);
开始
WriteLn((发送方为TGenericBase).Name);
结束;
//
//
//
var C:TGenericBase;
L:t总目录;
K:整数;
D:TGenericBase;
开始
尝试
L:=TGenericList.Create;
尝试
对于K:=0到10,开始
C:=TGenericBase.CreateValue(IntToStr(K),K);
L.AddBase(C);
结束;
对于K:=0到10,开始
D:=L.GetBase(IntToStr(K));
书面(D.数据);
结束;
最后
L.免费;
结束;
除了
关于E:Exception-do
Writeln(E.ClassName,“:”,E.Message);
结束;
ReadLn;
结束。

如果您释放了
L
,并且
L
也被用作接口参考,那么您就弄乱了接口的参考计数系统。这会导致你的问题

一般来说:除非您真的知道自己在做什么,否则不要将对象和接口引用混合到同一个对象中。后者不能用简单的答案来解释

简而言之:不要释放也用作接口的对象

当它不再被引用时,接口的自动引用计数最终将释放它。不要干涉那件事。如果在引用计数不为0时释放该项,则会出现“无效指针操作”错误,正如您所发现的那样


有关此方面的更多信息,请参阅Delphi文档:。此文档也可以在帮助文件中找到。

如果您释放了
L
,并且
L
也被用作接口参考,那么您就弄乱了接口的参考计数系统。这会导致你的问题

一般来说:除非您真的知道自己在做什么,否则不要将对象和接口引用混合到同一个对象中。后者不能用简单的答案来解释

简而言之:不要释放也用作接口的对象

program GenericTest;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, System.Generics.Collections;

type
  IObserver = interface
    ['{DD9243B9-0722-486A-B4BF-0929AB5B6627}']
    procedure ObservableChanged(Sender : TObject);
  end;

  IObservable = interface
    ['{39EA6448-6636-40F4-B618-740B0BB28127}']
    procedure RegisterObserver(Observer : IObserver);
    procedure UnregisterObserver(Observer : IObserver);
  end;

  TBase = class(TInterfacedObject, IObservable)
  private
    FName : String;
    FObservers : TList<IObserver>;
  public
    constructor Create(AName : String);
    destructor Destroy; override;
    procedure RegisterObserver(Observer : IObserver);
    procedure UnregisterObserver(Observer : IObserver);
    property Name : String read FName;
  end;

  TGenericBase = TBase;

  TGenericBase<T> = class(TGenericBase)
  private
    FData : T;
  public
    constructor Create(AName : String);
    constructor CreateValue(AName : String; AValue : T);
    property Data : T read FData write FData;
  end;

  TGenericList = class(TInterfacedObject, IObserver)
  private
    FBases : TObjectDictionary<String, TBase>;
  public
    constructor Create;
    destructor Destroy; override;
    procedure AddBase(ABase : TBase);
    function GetBase<T: TBase>(AName : String) : T;
    procedure ObservableChanged(Sender : TObject);
  end;

//
// TBase
//
constructor TBase.Create(AName: string);
begin
  inherited Create;
  FObservers := TList<IObserver>.Create();
  FName := AName;
end;

destructor TBase.Destroy;
begin
  if (FObservers <> nil) then FObservers.Free;
end;

procedure TBase.RegisterObserver(Observer : IObserver);
begin
  if (FObservers <> nil) then FObservers.Add(Observer);
end;

procedure TBase.UnregisterObserver(Observer : IObserver);
begin
  if (FObservers <> nil) then FObservers.Remove(Observer);
end;

//
// TGenericBase<T>
//
constructor TGenericBase<T>.Create(AName : String);
begin
  inherited Create(AName);
  FData := Default(T);
end;

constructor TGenericBase<T>.CreateValue(AName : String; AValue : T);
begin
  inherited Create(AName);
  FData := AValue;
end;

//
// TGenericList
//
constructor TGenericList.Create;
begin
  inherited Create;
  FBases := TObjectDictionary<String, TBase>.Create([doOwnsValues], 32);
end;

destructor TGenericList.Destroy;
begin
  if (FBases <> nil) then FBases.Free;
  inherited Destroy;
end;

procedure TGenericList.AddBase(ABase : TBase);
begin
  FBases.Add(ABase.Name, ABase);
  // Comment out this line and the error doesn't occur.
  ABase.RegisterObserver(Self);
end;

function TGenericList.GetBase<T>(AName : String) : T;
var C : TBase;
begin
  if not FBases.TryGetValue(AName, C) then
    raise Exception.Create('Couldn''t get base.');

  Result := C as T;
end;

procedure TGenericList.ObservableChanged(Sender : TObject);
begin
  WriteLn((Sender as TGenericBase).Name);
end;

//
//
//
var C : TGenericBase;
    L : TGenericList;
    K : Integer;
    D : TGenericBase<Double>;
begin
  try
    L := TGenericList.Create;
    try
      for K := 0 to 10 do begin
        C := TGenericBase<Double>.CreateValue(IntToStr(K), K);
        L.AddBase(C);
      end;

      for K := 0 to 10 do begin
        D := L.GetBase<TGenericBase<Double>>(IntToStr(K));
        WriteLn(D.Data);
      end;

    finally
      L.Free;
    end;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  ReadLn;
end.