这个问题是人们对stackoverflow的一个特定评论的延续,我已经看到了几个不同的时间现在。我,以及开发人员教我德尔福,为了保持安全,总是放一个检查如果assign()释放对象之前,在做其他各种事情之前。但是,我现在被告知,我不应该添加这个检查。我想知道如果应用程序编译/运行如果我这样做,或者如果它不会影响任何结果的任何差异…
if assigned(SomeObject) then SomeObject.Free;

让我们说,我有一个窗体,我在窗体的创建时在后台创建一个位图对象,当我完成它时释放它。现在我想我的问题是我习惯了把这个检查很多我的代码,当我试图访问的对象,可能在某个时候可能已经释放。我已经使用它,即使没有必要。我喜欢彻底…

unit Unit1;

interface

uses
  Windows,Messages,SysUtils,Variants,Classes,Graphics,Controls,Forms,Dialogs;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    FBitmap: TBitmap;
  public
    function LoadBitmap(const Filename: String): Bool;
    property Bitmap: TBitmap read FBitmap;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  FBitmap:= TBitmap.Create;
  LoadBitmap('C:\Some Sample Bitmap.bmp');
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  if assigned(FBitmap) then begin //<-----
    //Do some routine to close file
    FBitmap.Free;
  end;
end;

function TForm1.LoadBitmap(const Filename: String): Bool;
var
  EM: String;
  function CheckFile: Bool;
  begin
    Result:= False;
    //Check validity of file,return True if valid bitmap,etc.
  end;
begin
  Result:= False;
  EM:= '';
  if assigned(FBitmap) then begin //<-----
    if FileExists(Filename) then begin
      if CheckFile then begin
        try
          FBitmap.LoadFromFile(Filename);
        except
          on e: exception do begin
            EM:= EM + 'Failure loading bitmap: ' + e.Message + #10;
          end;
        end;
      end else begin
        EM:= EM + 'Specified file is not a valid bitmap.' + #10;
      end;
    end else begin
      EM:= EM + 'Specified filename does not exist.' + #10;
    end;
  end else begin
    EM:= EM + 'Bitmap object is not assigned.' + #10;
  end;
  if EM <> '' then begin
    raise Exception.Create('Failed to load bitmap: ' + #10 + EM);
  end;
end;

end.

现在让我们说,我引入一个新的自定义列表对象TMyListItem TMyList。对于这个列表中的每个项目,当然我必须创建/释放每个项目对象。有几种不同的方式创建一个项目,以及一些不同的方式来销毁一个项目(添加/删除是最常见的)。我相信这是一个很好的做法,把这种保护在这里…

procedure TMyList.Delete(const Index: Integer);
var
  I: TMyListItem;
begin
  if (Index >= 0) and (Index < FItems.Count) then begin
    I:= TMyListItem(FItems.Objects[Index]);
    if assigned(I) then begin //<-----
      if I <> nil then begin
        I.DoSomethingBeforeFreeing('Some Param');
        I.Free;
      end;
    end;
    FItems.Delete(Index);
  end else begin
    raise Exception.Create('My object index out of bounds ('+IntToStr(Index)+')');
  end;
end;

在许多情况下,至少我希望对象仍然创建之前,我尝试释放它。但是你永远不知道在未来某个对象在它应该发生之前被释放的情况下会发生什么。我一直用这个检查,但现在我被告知我不应该,我还是不明白为什么。

编辑

这里有一个例子,试图向你解释为什么我有这样做的习惯:

procedure TForm1.FormDestroy(Sender: TObject);
begin
  SomeCreatedobject.Free;
  if SomeCreatedobject = nil then
    ShowMessage('Object is nil')
  else
    ShowMessage('Object is not nil');
end;

我的观点是如果SomeCreatedobject<> nil与Assigned(SomeCreatedobject)不同,因为释放SomeCreatedobject后,它不会计算为nil。所以两个检查都应该是必要的。

解决方法

这是一个非常广泛的问题,有许多不同的角度。

Assigned函数的含义

你的问题中的大部分代码都错误地理解了Assigned函数。 documentation说:

Tests for a nil (unassigned) pointer or procedural variable.

Use
Assigned to determine whether the pointer or procedure referenced by P
is nil. P must be a variable reference of a pointer or procedural
type. Assigned(P) corresponds to the test P<> nil for a pointer
variable,and @P <> nil for a procedural variable.

Assigned returns
False if P is nil,True otherwise.

Note: Assigned cannot detect a
dangling pointer–that is,one that is not nil but no longer points to
valid data. For example,in the code example for Assigned,Assigned
does not detect that P is not valid.

从这里的关键点是:

> Assigned等价于测试<>零。
> Assigned无法检测指针或对象引用是否有效。

在这个问题的上下文中这是什么意思

if obj<>nil

if Assigned(obj)

是完全可互换的。

测试在调用Free之前分配

TObject.Free的实现非常特殊。

procedure TObject.Free;
begin
  if Self <> nil then
    Destroy;
end;

这允许你在一个为nil的对象引用上调用Free,这样做没有任何效果。对于什么是值得的,我知道在RTL / VCL没有其他地方使用这样的把戏。

你想要允许Free在nil对象引用上调用的原因源于构造函数和析构函数在Delphi中操作的方式。

当在构造函数中引发异常时,将调用析构函数。这是为了释放分配在成功的那部分构造函数中的任何资源。如果Free没有实现,因为它是那么析构函数必须看起来像这样:

if obj1 <> nil then
  obj1.Free;
if obj2 <> nil then
  obj2.Free;
if obj3 <> nil then
  obj3.Free;
....

下一个拼图是Delphi constructors initialise the instance memory to zero.这意味着任何未分配的对象引用字段为零。

把这一切放在一起,析构函数代码现在变成了

obj1.Free;
obj2.Free;
obj3.Free;
....

你应该选择后一个选项,因为它更容易阅读。

有一种情况,您需要测试参考是否在析构函数中分配。如果你需要调用对象上的任何方法,在销毁它之前,然后清楚你必须防止它的可能性为零。所以这个代码将运行AV的风险,如果它出现在析构函数中:

FSettings.Save;
FSettings.Free;

相反你写

if Assigned(FSettings) then
begin
  FSettings.Save;
  FSettings.Free;
end;

测试在析构函数外部分配

你还谈谈在析构函数之外编写防御性代码。例如:

constructor TMyObject.Create;
begin
  inherited;
  FSettings := TSettings.Create;
end;

destructor TMyObject.Destroy;
begin
  FSettings.Free;
  inherited;
end;

procedure TMyObject.Update;
begin
  if Assigned(FSettings) then
    FSettings.Update;
end;

在这种情况下,再次不需要在TMyObject.Update中测试Assigned。原因是你根本不能调用TMyObject.Update,除非TMyObject的构造函数成功。如果TMyObject的构造函数成功,那么你肯定知道分配了FSettings。所以再次,你让你的代码更少的可读性和更难维护通过对Assigned的虚假调用。

有一种情况,你需要写如果Assigned,并且这是有问题的对象的存在是可选的。例如

constructor TMyObject.Create(UseLogging: Boolean);
begin
  inherited Create;
  if UseLogging then
    FLogger := TLogger.Create;
end;

destructor TMyObject.Destroy;
begin
  FLogger.Free;
  inherited;
end;

procedure TMyObject.FlushLog;
begin
  if Assigned(FLogger) then
    FLogger.Flush;
end;

在这种情况下,类支持两种操作模式,包括和不包括日志记录。决定是在建设时进行的,任何引用记录对象的方法都必须测试它的存在。

这种不常见的代码形式使得更重要的是不要使用对非可选对象的Assigned的伪调用。当你看到代码中的Assigned(FLogger)时,应该清楚地表明类可以正常运行,而FLogger不存在。如果你对你的代码进行免费的调用,那么你就失去了一个对象总是存在的能力。

delphi – 为什么我不应该使用“如果Assigned()”之前使用或释放​​的东西?的更多相关文章

  1. 在ios上使用来自框架的boost :: filesysystem路径

    我一直在使用Boost作为PeteGoodliffe脚本构建的框架已有一段时间了.效果很好.最近我遇到了一个问题,可以通过将以下代码放入另一个全新的XCode项目中的视图控制器的viewDidLoad中来重现:当路径对象被销毁时会导致EXC_BAD_ACCESS.有没有其他人遇到这个问题?

  2. Xcode C开发,需要澄清

    我非常喜欢Xcode提供对该语言可能的成员函数的深入了解的方式,并且更喜欢相对于文本伙伴使用它,如果不是因为我今天注意到的奇怪.当strings=“Teststring”时;唯一可用的substr签名如图所示但据我所知,签名应该是什么iseeonline确实s.substr(1,2);既被理解也适用于Xcode.当我尝试方法完成时为什么不显示?

  3. xamarin.ios – 没有找到ViewController ::.ctor(System.IntPtr)的构造函数

    我有一个问题,我的Monotouch应用程序有时在收到内存警告后才会崩溃.请参见下面的堆栈跟踪.堆栈跟踪是正确的,因为指定的类缺少构造函数获取IntPtr参数.但是这是有意的,因为我在应用程序中根本不使用InterfaceBuilder.那为什么会这样呢?

  4. ios – 我在哪里可以找到用于创建IPad应用程序的Delphi资源?

    我之前一直在使用Delphi并且一直都是Windows家伙.我的妻子为我的生日买了一台新的iPad,我昨晚第一次使用它.哇!…

  5. ios – Swift – NSURL错误

    尝试使用下面的NSURL类时出错,下面的代码实际上是试图将我从Facebook拉入的图像存储到imageView中.错误如下:不知道为什么会这样,帮忙!解决方法你正在调用的NSURL构造函数有这个签名:?表示构造函数可能不返回值,因此它被视为可选.NSData构造函数也是如此:快速解决方法是:最好的解决方案是检查(解包)这些选项,即使您确定它们包含值!

  6. 如何从命令行部署OSX或IOS Delphi项目?

    我正在使用像这样的脚本构建我的Delphi应用程序现在我想添加一个选项将应用程序部署到OSX系统修改这样的脚本,那么可以从命令行部署OSX或IOSDelphi项目吗?

  7. 如何在Xcode中追踪“libc abi.dylib:纯虚函数!”

    我有一个多线程OSX应用程序,它使用C,Objective-C和Swift的混合.当我的应用程序关闭时,我在Xcode调试器窗口中看到了这一点:我知道这个错误通常是由对C类构造函数或析构函数中的虚函数的调用引起的.有没有一种简单的方法可以找到它的位置?

  8. Swift实现对象归档

    Swift实现对象归档时有几个注意点要继承NSCoding,实现两个方法extension是一个分类,分类不允许有存储能力,所以协议方法不能写在分类中协议中的init(coderdecoder:NSCoder)函数会覆盖原始的构造函数,所以类中至少还要有另一个init方法如果不指定键名,会使用属性名称作为key,基本数据类型,需要指定key

  9. 【Swift初见】Swift构造过程

    构造过程是通过构造器来实现的,其实每个构造器就可以看作是一个函数,只是这个函数是为了执行初始化的。每个类都必须拥有一个指定构造器。

  10. swift的struct结构体类型介绍使用

随机推荐

  1. delphi – 主窗口按进程名称处理

    DelphiXe,Win7x64如何从进程名称(exe文件的完整路径)获取主窗口句柄,或至少一个类或窗口名称(如果该进程只有一个窗口).例:解决方法我同意Petesh的说法,你需要枚举顶级窗口并检查创建它的进程的模块文件名.为了帮助您开始枚举顶级窗口,这是一个delphi实现.首先,当你回调给你时,你需要一些与EnumWindows方法通信的方式.为此声明一条记录,该记录将保存您要查找的模块的文件

  2. 如何在Delphi中纯粹通过RTTI信息(即不使用任何实际对象实例)获取TObjectList的子项类型?

    我正在使用RTTI实现用于流式传输任意Delphi对象的通用代码,并且为了使其工作(更具体地说,为了使加载部分工作),我需要以某种方式获得TObjectList的子项类型<T>不使用任何实际对象实例的字段.要求不使用任何实际对象实例的明显原因是,在从流加载对象的情况下(仅基于要加载的对象的类类型的知识),我将不会有任何实例在加载完成之前完全可用–我宁愿只能访问相关类的纯RTTI数据.我希望能

  3. inno-setup – Inno Setup – 安装程序背景图片

    图像作为安装程序背景如何用inno5.5.9做到这一点?

  4. inno-setup – Inno Setup – 如何添加多个arc文件进行解压缩?

    使用InnoSetup解压缩弧文件.我希望有可能解压缩多个arc文件以从组件选择中安装文件(例如).但仍然显示所有提取的整体进度条.这可能吗?的回答的修改预备是相同的,参考其他答案.在ExtractArc中,为要提取的每个存档调用AddArchive.

  5. delphi – 如何在DataSet的帮助下在TAdvStringGrid中显示数据库中的BLOB图像

    解决方法CreateBlobStream正在创建一个TStream对象,而不是TMemoryStream.由于您不想将JPG写入数据库,因此应使用bmRead而不是bmReadWrite.我不习惯sqlite,但你必须确保使用合适的二进制日期类型.为了确保存储的图像真的是JPG,您应该编写JPG以进行测试,例如:

  6. inno-setup – 在Inno Setup的Code部分下载程序后运行程序

    如何运行我通过Internet下载的应用程序,在代码部分中使用,并等待该应用程序完成运行.我有,使用InnoTools下载程序,下载这两个文件,我想,在第二个完成下载后运行该下载,或jdk-8u111-windows-x64.exe,然后继续安装.解决方法使用其他下载插件,而不是ITD(请参阅下面的原因).例如,InnoDownloadPlugin.当您包含idp.iss时,它定义了一个全局IDP

  7. progress-bar – Inno Setup Run部分的简单进度页面

    我的安装程序非常简单,它基本上是:>欢迎页面>进展页面>最终页面欢迎页面和最终页面是标准页面.在Progress页面,我正在静默安装一堆其他程序.实际的脚本是在[Run]部分中安装每个程序.问题是酒吧达到100%然后停留在那里.我只能更改消息文本.我想要实现的是使用Pascal脚本显示进度,例如:这样我就可以显示更准确的进度条.这就是我所拥有的:问题是,当我构建安装程序时,它不显示欢迎页面.我做错了什么?

  8. delphi – 如何使“显示/隐藏桌面图标”设置生效?

    下面的代码调用SHGetSetSettings函数来隐藏桌面图标但它只是从视图菜单中取消选中“显示桌面图标”.我打电话给SHChangeNotify;更新桌面,但这不起作用?解决方法isa,要刷新桌面,您可以将F5键发送到progman窗口隐藏桌面图标的另一种方法是再次显示

  9. inno-setup – Inno Setup – 避免显示子安装程序的文件名

    我试图使用InnoSetup–Howtohidecertainfilenameswhileinstalling?(FilenameLabel)的想法Theonlysuresolutionistoavoidinstallingthefiles,youdonotwanttoshow,usingthe[Files]section.Installthemusingacodeinstead.UsetheEx

  10. inno-setup – Inno Setup磁力链接下载实施

    我目前正在使用InnoDownloadPlugin为我的安装程序下载文件,这个问题最大的问题是faila正确下载文件.因为连接不良等诸多原因.我想添加一种替代方法来下载文件,因此用户可以选择是否需要常规方式或torrent方式.我知道我可以使用aria2c.exe应用程序(https://aria2.github.io/),有人可以帮我实现它的inno设置代码吗?我需要的是使用torrent(ar

返回
顶部