调试时为什么我的全局变量“无法访问”?

时间:2014-06-02 14:10:36

标签: delphi variables global

我正在构建一个包含大约30个表单的应用程序。我需要管理会话,所以我希望可以从所有表单访问全局LoggedInUser变量。 我读了“David Heffernan”关于全局变量的帖子,以及如何避免它们,但我认为拥有一个全局User变量而不是30个具有自己的User变量的表单会更容易。

所以我有一个单位:GlobalVars

unit GlobalVars;

interface
uses User; // I defined my TUser class in a unit called User
var
   LoggedInUser: TUser;

implementation

initialization
   LoggedInUser:= TUser.Create;
finalization
   LoggedInUser.Free;
end.

然后在我的LoginForm的LoginBtnClick程序中,我做了:

unit FormLogin;

interface

uses
  [...],User;

type
  TForm1 = class(TForm)
   [...]
    procedure LoginBtnClick(Sender: TObject);
  private
    { Déclarations privées }
  public
  end;

var
  Form1: TForm1;
  AureliusConnection : IDBConnection;

implementation

{$R *.fmx}
uses [...]GlobalVars;

procedure TForm1.LoginBtnClick(Sender: TObject);
var
  Manager : TObjectManager;
  MyCriteria: TCriteria<TUser>;
  u : TUser;
begin
  Manager := TObjectManageR.Create(AureliusConnection);
   MyCriteria :=Manager.Find<TUtilisateur>
    .Add(TExpression.Eq('login',LoginEdit.Text))
    .Add(TExpression.Eq('password',PasswordEdit.Text));
  u := MyCriteria.UniqueResult;
  if u = nil then
   MessageDlg('Login ou mot de passe incorrect',TMsgDlgType.mtError,[TMsgDlgBtn.mbOK],0)
  else begin
   LoggedInUser:=u;     //Here I assign my local User data to my global User variable
   Form1.Destroy;
   A00Form.visible:=true;
  end;
  Manager.Free;
end;

然后在另一种形式中,我想在Menu1BtnClick过程中访问此LoggedInUser对象:

Unit C01_Deviations;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, 
  FMX.Types, FMX.Graphics, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls,
  FMX.ListView.Types, FMX.ListView, FMX.Objects, FMX.Layouts, FMX.Edit, FMX.Ani;

type
  TC01Form = class(TForm)
   [...]
Menu1Btn: TButton;
[...]
procedure Menu1BtnClick(Sender: TObject);

  private
    { Déclarations privées }
  public
    { Déclarations publiques }
  end;

var
  C01Form: TC01Form;

implementation
uses [...]User,GlobalVars;
{$R *.fmx}

procedure TC01Form.Menu1BtnClick(Sender: TObject);
var
  Assoc : TUtilisateur_FonctionManagement;
  ValidationOK : Boolean;
  util : TUser;
begin
  ValidationOK := False;
  util := GlobalVars.LoggedInUser; // Here i created a local user variable for debug purposes as I thought it would permit me to see the user data. But i get "Inaccessible Value" as its value
  util.Nom:='test';
  for Assoc in util.FonctionManagement do  // Here is were my initial " access violation" error occurs
  begin
    if Assoc.FonctionManagement.Libelle = 'Reponsable équipe HACCP' then
    begin
      ValidationOK := True;
      break;
    end;
  end;
  [...]
end;

当我调试时,我在用户的值列中看到“Inaccessible Value”。你知道为什么吗?

我试图在这个GlobalVar单元中放置一个整数,我可以从我的登录表单中设置它的值,并从我的其他表单中读取它。

我想我可以存储用户的id,这是一个整数,然后使用其id从数据库中检索用户。但它似乎真的很无效。

3 个答案:

答案 0 :(得分:5)

“我将此数据分配给我的全球用户” - 这是什么意思?

通过我的水晶球,我在您的登录表单中看到与此相似的代码:

var
  user: TUser;
begin
  user := TUser.Create;
  try
    // assign properties/fields of user instance

    LoggedInUser := user;
  finally
    user.Free;
  end;
end;

之后,LoggedInUser指向已释放(可能重用)的内存块。处理LoggedInUser的任何属性/字段可能会导致访问冲突。

哈,非常接近:

LoggedInUser:=u;

Assign(aSource: TUser)类添加方法TUser,该类为所有字段/属性(不是引用分配)执行值的深层副本,并改为调用它:

LoggedInUser.Assign(u);

答案 1 :(得分:1)

或许可以添加更多解释,请考虑以下代码部分:

begin
  Manager := TObjectManageR.Create(AureliusConnection);
   MyCriteria :=Manager.Find<TUtilisateur>
    .Add(TExpression.Eq('login',LoginEdit.Text))
    .Add(TExpression.Eq('password',PasswordEdit.Text));
  u := MyCriteria.UniqueResult;

现在,TUser是一个引用类型,因此变量u包含指向该对象的指针。在这种情况下,u现在指向属于您的UniqueResult实例的TObjectManager对象。

  if u = nil then
   MessageDlg('Login ou mot de passe incorrect',TMsgDlgType.mtError,[TMsgDlgBtn.mbOK],0)
  else begin
   LoggedInUser:=u;     

//Here I assign my local User data to my global User variable

没有。在这里,您还要LoggedInUser指向u所指向的同一对象实例,该实例仍然是UniqueResult的{​​{1}}。此外,由于TObjectManager中的initialization部分已经创建了GlobalVars的实例(TUser指向的实例),您刚刚覆盖了该引用并泄露了内存。

LoggedInUser

现在你释放了 Form1.Destroy; A00Form.visible:=true; end; Manager.Free; end; ,摧毁了TObjectManager - UniqueResultu所指向的对象。

Igor建议执行深层复制将解决这两个问题。

LoggedInUser

首先,您不会泄漏内存,因为您现在将为以前构造的 LoggedInUser.Assign(u); 对象分配值(而不是覆盖您对它的唯一引用!)。其次,当您的TUser被释放时,您不会销毁TObjectManager引用的对象。

如果LoggedInUser是自定义类,则需要实现自己的深层复制方法。见here for an example。您不需要从TUser派生,也不需要使用TPersistent,但您需要在Assign课程中提供某种类型的深层复制功能。

或者,from the documentation,看看

  

OwnsObjects属性

     

如果为true(默认值),则在销毁时会销毁所有托管对象   TObjectManager对象被销毁。如果为false,则对象保留   存储器中。

因此,在您释放TUser之前,只需执行以下操作:

TObjectManager

小心释放您不再需要的查询等返回的任何其他对象。这将允许您像现在一样分配引用,它将阻止Manager.OwnsObjects := false; Manager.Free; 在它自身被销毁时释放它。

请注意,此解决方案仍然会让您遇到内存泄漏问题,因此应该通过检查实例并在进行新分配之前释放它或者不构建空TObjectManager来处理它。

答案 2 :(得分:1)

因为您要在全局变量中创建一个对象实例,这表示您希望稍后为其分配值,而不是分配一个全新的对象。

您可以使用.Assign方法按照建议执行此操作,或者如果只有一些方法,您可以逐个复制相关字段。

你想要一个&#34;深拷贝&#34; TUser对象的字段,并确保如果TUser中有任何对象,您可以确定是否需要进行深度复制或者是否可以复制引用。

但这是人们常犯的错误。你想要一个或者另一个 - 创建一个实例并将VALUES复制到其中;或者不要创建实例并为其分配另一个实例,就像从TObjectManager获得的实例一样。但正如同样指出的那样,TObjectManager返回的生命周期可能不是你所期望的。 (如果它使用一个接口,它会自动引用计数并且可以安全地分配给另一个对象。但是如果不考虑是否是这种情况,它可能并不明显。)

由于TObjectManager返回对象引用或NIL,您只需将TObjectManager的值直接分配给全局变量即可。在这种情况下,请不要在初始化部分创建默认实例。

我也回应了将对象传递给表单的contstructors而不是使用全局变量的情绪。这可以让你设置单元测试。

我还想添加其他人没有提及的评论。

  else begin
   LoggedInUser:=u;     //Here I assign my local User data to my global User variable
   Form1.Destroy;
   A00Form.visible:=true;
  end;

这不是你破坏表格的方式!因为从技术上讲,在调用Form1.Destroy之后没有任何内容在有效实例内运行。它大部分时间都可能正常工作,因为堆栈可能没有被破坏;但是任何人都在猜测A00Form.visible:= true是否会起作用。

并且......那不是你如何关闭一个表格而是将焦点放在另一个表格上。这个表格不应该知道任何其他形式。同样,如果需要创建表单时应该注入的东西,在这种情况下它实际上不是。

一般来说,使用OnClose处理程序来完成表单关闭时要发生的事情。但在这种情况下,你甚至不想要那样。

您想要从其他地方实例化表单,然后使用ShowModal来显示它。当它关闭时,您需要设置某种返回状态,以指示该人是否正确登录。如果他们这样做了,那么打开你希望他们看到的下一个表格。如果没有,您可能希望再次向他们显示登录表单,并在其上显示一条消息。

这也暗示了如何将TUser记录注入到每个表单中 - 通过将表单打包在创建表单实例的方法内部,传入TUser对象(无论是通过构造函数还是属性),然后省去ShowModal返回后的表单。这可能意味着您只想隐藏关闭而不是免费的表单,允许控制Display方法在销毁表单之前访问表单数据! (我相信caHide是OnClose处理程序的默认Action,所以如果你想用caFree替换它,你只需要添加一个OnClose。那是因为IDE的默认行为是自动的 - 创建表单,在这种情况下,你不希望他们在关闭时释放自己。如果你不自动创建你的表单,那么你肯定需要在创建它们之后释放它们并用Show显示它们或ShowModal - 除非你选择让它们在内存中徘徊,否则会破坏首先绕过自动创建的目的。)