Delphi XE t字节的正确用法

Delphi XE t字节的正确用法,delphi,delphi-xe,Delphi,Delphi Xe,TBytes变量的正确使用模式是什么?根据我的理解,TBytes不是一个类,而是一个“动态字节数组”。我不确定在哪里为它分配内存,何时释放内存,以及哪种方式是将内存从生产者传递给消费者的最佳方式。我希望生产者创建一个TBytes实例,然后将其传递给消费者。发生这种情况后,生产者希望重用其TBytes成员变量,即消费者最终将内存返回给系统的内容。如果TBytes是一个对象,我不会有任何问题,但我不确定TBytes在这个场景中是如何工作的 例如,在对象A中,我想将一些数据组合到作为对象A成员的TBy

TBytes变量的正确使用模式是什么?根据我的理解,TBytes不是一个类,而是一个“动态字节数组”。我不确定在哪里为它分配内存,何时释放内存,以及哪种方式是将内存从生产者传递给消费者的最佳方式。我希望生产者创建一个TBytes实例,然后将其传递给消费者。发生这种情况后,生产者希望重用其TBytes成员变量,即消费者最终将内存返回给系统的内容。如果TBytes是一个对象,我不会有任何问题,但我不确定TBytes在这个场景中是如何工作的

例如,在对象A中,我想将一些数据组合到作为对象A成员的TBytes数组中。完成后,我想将TBytes数组传递给另一个对象B,该对象B随后成为数据的所有者。同时,回到对象A,我想开始组装更多数据,重用TBytes成员变量

type
  TClassA = class
  private
    FData: TBytes;
  public
    procedure AssembleInput(p: Pointer; n: Cardinal);
  end;

  TClassB = class
  public
    procedure ProcessData(d: TBytes);
  end;

var
  a: TClassA;
  b: TClassB;

procedure TClassA.AssembleInput(p: Pointer; n: Cardinal);
begin
  SetLength(FData, n);
  Move(p^, FData, n);  // Is this correct?
  ...
  b.ProcessData(FData);

  ...

  // Would it be legal to reuse FData now?  Perhaps by copying new (different)
  // data into it?
end;

procedure TClassB.ProcessData(d: TBytes);
begin
  // B used the TBytes here.  How does it free them?
  SetLength(d, 0);  // Does this free any dynamic memory behind the scenes?
end;

提前谢谢

Delphi动态数组是具有自动生存期管理的托管类型。它们是引用计数的,当引用计数变为0时,它们被释放。您可以认为它们在字符串、接口和变体方面是等价的

可以通过以下三种方式之一显式释放对动态数组的引用:

a := nil;
Finalize(a);
SetLength(a, 0);
然而,当变量离开作用域时,不执行任何操作并释放引用是非常常见的

使用动态数组需要注意的一件事是,当您对同一个动态数组有两个引用时。在这种情况下,通过一个引用应用的更改从另一个引用可见,因为只有一个对象

SetLength(a, 1);
a[0] := 42;
b := a;
b[0] := 666;//now a[0]=666

您会问这是否正确:

Move(p^, FData, n);
不,不是。您在这里所做的是将
p
的内容复制到参考
FData
上。如果要使用
Move
进行复制,则可以编写:

Move(p^, Pointer(FData)^, n);
if n>0 then 
  Move(p^, FData[0], n);
或者,如果您希望更详细一点,避免演员阵容,您可以写:

Move(p^, Pointer(FData)^, n);
if n>0 then 
  Move(p^, FData[0], n);
我个人并不觉得演员阵容太糟糕,因为无论怎样,
Move
绝对没有类型安全性


现在重用FData合法吗?也许是通过复制新的(不同的)数据

我觉得没有更多的背景,我无法回答这个问题。例如,我不知道为什么
FData
是一个字段,因为它只在本地用于该函数。作为一个局部变量,它更有意义。它被声明为字段可能是有原因的,但从这段代码中很难看出它


您需要了解如何使用生产者/消费者模式。通常,这样做是为了使生产与消费脱钩。然而,您的示例代码并没有做到这一点,可能是因为解耦的代码过于复杂,无法包含在这里

对于真正的生产者/消费者实现,您需要将数据的所有权从生产者转移到消费者。根据我们上面所描述的,一种非常简单有效的方法是使用引用计数。当数据传输到消费者时,生产者应释放对其的引用。

Move(p^,FData,n);这没关系

过程TClassB.ProcessData(d:t字节);//d是FData的引用计数 开始 //d不保存任何内容,但FData保持不变,refcount=1 //如果您将“var”关键字放在d前面,FData将被释放 设定长度(d,0)

结束

您的代码中有几个误用。以下几点更为正确:

type
  TClassA = class
  private
    FData: TBytes;
  public
    procedure AssembleInput(p: Pointer; n: NativeUInt);
  end;

  TClassB = class
  public
    procedure ProcessData(var d: TBytes);
  end;

var
  a: TClassA;
  b: TClassB;

procedure TClassA.AssembleInput(p: Pointer; n: NativeUInt);
begin
  SetLength(FData, n);
  if n <> 0 then Move(p^, FData[0], n);
  ...
  b.ProcessData(FData);
  // FData is ready for reuse here...
end;

procedure TClassB.ProcessData(var d: TBytes);
begin
  ...
  SetLength(d, 0);
end;
类型
TClassA=类
私有的
FData:TBytes;
公众的
过程汇编输入(p:指针;n:本机输入);
结束;
TClassB=class
公众的
过程数据(变量d:t字节);
结束;
变量
答:塔克拉萨;
b:TClassB;
过程TClassA.AssembleInput(p:Pointer;n:NativeUInt);
开始
设定长度(FData,n);
如果n0,则移动(p^,FData[0],n);
...
b、 ProcessData(FData);
//FData已准备好在此处重复使用。。。
结束;
过程TClassB.ProcessData(变量d:TBytes);
开始
...
设定长度(d,0);
结束;

不,不正常。它将字节从p中的地址移动到FData中的指针。应该是FData[0]。感谢您的解释。是的,FData是成员变量的原因是生产者从TCP套接字获取数据,因此每次调用时通常只获取部分数据。是的,我确实想要生产者/消费者,关于“需要转移数据所有权”的评论正是我想要做的。是的,解耦的代码太复杂了;因此简化了。最后,我仍然不确定如何正确地“转移所有权”——你能澄清一下这个问题吗?让消费者参考一下,然后发布生产者的参考。Consumer.data:=FData;FData:=零;